diff --git a/files.html b/files.html
index 1c6846410d71a87ebc96d8364f402e2e95478bb3..84770acc08e22454fe9c8b8752b61d0904b40c96 100644
--- a/files.html
+++ b/files.html
@@ -73,6 +73,7 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/cube'>cube</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/cylinder'>cylinder</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/function'>function</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/pyramid'>pyramid</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/sphere'>sphere</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/frep/shapes/3D/torus'>torus</a><br>
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transforms</i><br>
diff --git a/modules/frep/shapes/3D/pyramid b/modules/frep/shapes/3D/pyramid
new file mode 100755
index 0000000000000000000000000000000000000000..1c740ac533db7b269643f25d3701e30251194ff6
--- /dev/null
+++ b/modules/frep/shapes/3D/pyramid
@@ -0,0 +1,173 @@
+//
+// frep pyramid
+//
+// Neil Gershenfeld
+// (c) Massachusetts Institute of Technology 2018
+//
+// This work may be reproduced, modified, distributed, performed, and
+// displayed for any purpose, but must acknowledge the mods
+// project. Copyright is retained and must be preserved. The work is
+// provided as is; no warranty is provided, and users accept all
+// liability.
+//
+// closure
+//
+(function(){
+//
+// module globals
+//
+var mod = {}
+//
+// name
+//
+var name = 'frep pyramid'
+//
+// initialization
+//
+var init = function() {
+   mod.x0.value = '0'
+   mod.x1.value = '1'
+   mod.y0.value = '0'
+   mod.y1.value = '1'
+   mod.z0.value = '0'
+   mod.z1.value = '1'
+   }
+//
+// inputs
+//
+var inputs = {
+   variables:{type:'',
+      event:function(evt){
+         for (var p in evt.detail)
+            mod[p].value = evt.detail[p]
+         outputs.variables.event()
+         outputs.shape.event()
+         }}}
+//
+// outputs
+//
+var outputs = {
+   shape:{type:'',
+      event:function(){
+         var x0 = parseFloat(mod.x0.value)
+         var x1 = parseFloat(mod.x1.value)
+         var y0 = parseFloat(mod.y0.value)
+         var y1 = parseFloat(mod.y1.value)
+         var z0 = parseFloat(mod.z0.value)
+         var z1 = parseFloat(mod.z1.value)
+         var fnx0 = `X-(${x0})-((Z-(${z0}))/((${z1})-(${z0})))*((${x1})-(${x0}))/2`
+         var fny0 = `Y-(${y0})-((Z-(${z0}))/((${z1})-(${z0})))*((${y1})-(${y0}))/2`
+         var fnx1 = `(${x1})-X-((Z-(${z0}))/((${z1})-(${z0})))*((${x1})-(${x0}))/2`
+         var fny1 = `(${y1})-Y-((Z-(${z0}))/((${z1})-(${z0})))*((${y1})-(${y0}))/2`
+         var fn = `Math.min(${fnx0},${fnx1})`
+         fn = `Math.min(${fny0},${fn})`
+         fn = `Math.min(${fny1},${fn})`
+         fn = `Math.min(Z-(${z0}),${fn})`
+         fn = `Math.min((${z1})-Z,${fn})`
+         var variables = ['X','Y','Z']
+         var limits = [[x0,x1],[y0,y1],[z0,z1]]
+         var type = 'Magnitude'
+         var shape = {function:fn,variables:variables,limits:limits,type:type}
+         mods.output(mod,'shape',shape)}
+         },
+   variables:{type:'',
+      event:function(){
+         var x0 = parseFloat(mod.x0.value)
+         var x1 = parseFloat(mod.x1.value)
+         var y0 = parseFloat(mod.y0.value)
+         var y1 = parseFloat(mod.y1.value)
+         var z0 = parseFloat(mod.z0.value)
+         var z1 = parseFloat(mod.z1.value)
+         var vars = {x0:x0,x1:x1,y0:y0,y1:y1,z0:z0,z1:z1}
+         mods.output(mod,'variables',vars)}
+         }}
+//
+// interface
+//
+var interface = function(div){
+   mod.div = div
+   //
+   // x0
+   //
+   div.appendChild(document.createTextNode('x0: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.x0 = input
+   //
+   // x1
+   //
+   div.appendChild(document.createTextNode(' x1: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.x1 = input
+   div.appendChild(document.createElement('br'))
+   //
+   // y0
+   //
+   div.appendChild(document.createTextNode('y0: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.y0 = input
+   //
+   // y1
+   //
+   div.appendChild(document.createTextNode(' y1: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.y1 = input
+   div.appendChild(document.createElement('br'))
+   //
+   // z0
+   //
+   div.appendChild(document.createTextNode('z0: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.z0 = input
+   //
+   // z1
+   //
+   div.appendChild(document.createTextNode(' z1: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.z1 = input
+   div.appendChild(document.createElement('br'))
+   //
+   // output button
+   //
+   var btn = document.createElement('button')
+      btn.style.padding = mods.ui.padding
+      btn.style.margin = 1
+      btn.appendChild(document.createTextNode('output'))
+      btn.addEventListener('click',function(){
+         outputs.variables.event()
+         outputs.shape.event()
+         })
+      div.appendChild(btn)
+   }
+//
+// local functions
+//
+;
+//
+// return values
+//
+return ({
+   name:name,
+   init:init,
+   inputs:inputs,
+   outputs:outputs,
+   interface:interface
+   })
+}())
diff --git a/modules/index.js b/modules/index.js
index 4e7e193aa255021ce6286d62f38fd0c4698d0ed5..d7cd2c05777101e878ce3cf252de0a59694f0bcc 100644
--- a/modules/index.js
+++ b/modules/index.js
@@ -43,6 +43,7 @@ module_menu('         cone','modules/frep/shapes/3D/cone')
 module_menu('         cube','modules/frep/shapes/3D/cube')
 module_menu('         cylinder','modules/frep/shapes/3D/cylinder')
 module_menu('         function','modules/frep/shapes/3D/function')
+module_menu('         pyramid','modules/frep/shapes/3D/pyramid')
 module_menu('         sphere','modules/frep/shapes/3D/sphere')
 module_menu('         torus','modules/frep/shapes/3D/torus')
 module_label('   transforms')