diff --git a/files.html b/files.html
index 1110563702f28b645fb2b588c05643d1b112e54c..af78a9a0da99ba62b15438875bd5ca54cdaefc28 100644
--- a/files.html
+++ b/files.html
@@ -19,6 +19,7 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/files.js'>files.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/mods.js'>mods.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/modules.js'>modules.js</a><br>
+<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;node_modules</i><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/printserver.js'>printserver.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/programs.js'>programs.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/serialserver.js'>serialserver.js</a><br>
@@ -64,6 +65,7 @@
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2D</i><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/2D/circle'>circle</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/2D/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/2D/polygon'>polygon</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/2D/rectangle'>rectangle</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/2D/right%20triangle'>right triangle</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/2D/triangle'>triangle</a><br>
diff --git a/modules/frep/shapes/2D/circle b/modules/frep/shapes/2D/circle
index e79bb33570388d298a420ab5dee51024f7ab684d..66cad6c131c0885e8229ac4d9c27849bd04f9644 100755
--- a/modules/frep/shapes/2D/circle
+++ b/modules/frep/shapes/2D/circle
@@ -27,7 +27,7 @@ var name = 'frep circle'
 var init = function() {
    mod.x.value = '0'
    mod.y.value = '0'
-   mod.r.value = '1'
+   mod.radius.value = '1'
    }
 //
 // inputs
@@ -48,10 +48,10 @@ var outputs = {
       event:function(){
          var x = parseFloat(mod.x.value)
          var y = parseFloat(mod.y.value)
-         var r = parseFloat(mod.r.value)
-         var fn = `((${r})*(${r})-((X-(${x}))*(X-(${x}))+(Y-(${y}))*(Y-(${y}))))`
+         var radius = parseFloat(mod.radius.value)
+         var fn = `((${radius})*(${radius})-((X-(${x}))*(X-(${x}))+(Y-(${y}))*(Y-(${y}))))`
          var variables = ['X','Y']
-         var limits = [[x-r,x+r],[y-r,y+r]]
+         var limits = [[x-radius,x+radius],[y-radius,y+radius]]
          var type = 'Magnitude'
          var shape = {function:fn,variables:variables,limits:limits,type:type}
          mods.output(mod,'shape',shape)}
@@ -60,8 +60,8 @@ var outputs = {
       event:function(){
          var x = parseFloat(mod.x.value)
          var y = parseFloat(mod.y.value)
-         var r = parseFloat(mod.r.value)
-         var vars = {x:x,y:y,r:r}
+         var radius = parseFloat(mod.radius.value)
+         var vars = {x:x,y:y,radius:radius}
          mods.output(mod,'variables',vars)}
          }}
 //
@@ -91,12 +91,12 @@ var interface = function(div){
    //
    // r
    //
-   div.appendChild(document.createTextNode('r: '))
+   div.appendChild(document.createTextNode('radius: '))
    var input = document.createElement('input')
       input.type = 'text'
       input.size = 3
       div.appendChild(input)
-      mod.r = input
+      mod.radius = input
    div.appendChild(document.createElement('br'))
    //
    // output button
diff --git a/modules/frep/shapes/2D/polygon b/modules/frep/shapes/2D/polygon
new file mode 100755
index 0000000000000000000000000000000000000000..d866aa9185141dc476a613faf67801566cf3d149
--- /dev/null
+++ b/modules/frep/shapes/2D/polygon
@@ -0,0 +1,147 @@
+//
+// frep polygon 
+//    todo: tight bounding box calculation
+//
+// 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 polygon'
+//
+// initialization
+//
+var init = function() {
+   mod.x.value = '0'
+   mod.y.value = '0'
+   mod.sides.value = '5'
+   mod.radius.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 x = parseFloat(mod.x.value)
+         var y = parseFloat(mod.y.value)
+         var sides = parseInt(mod.sides.value)
+         var radius = parseFloat(mod.radius.value)
+         var fn = `((${radius})-(X-(${x})))`
+         for (var s = 1; s < sides; ++s) {
+            var nx = Math.cos(s*2*Math.PI/sides)
+            var ny = Math.sin(s*2*Math.PI/sides)
+            var fn = `Math.min((${radius})-((X-(${x}))*(${nx})+(Y-(${y}))*(${ny})),${fn})`
+            }
+         var variables = ['X','Y']
+         var limits = [[x-2*radius,x+2*radius],[y-2*radius,y+2*radius]]
+         var type = 'Magnitude'
+         var shape = {function:fn,variables:variables,limits:limits,type:type}
+         mods.output(mod,'shape',shape)}
+         },
+   variables:{type:'',
+      event:function(){
+         var x = parseFloat(mod.x.value)
+         var y = parseFloat(mod.y.value)
+         var sides = parseFloat(mod.sides.value)
+         var radius = parseFloat(mod.radius.value)
+         var vars = {x:x,y:y,sides:sides,radius:radius}
+         mods.output(mod,'variables',vars)}
+         }}
+//
+// interface
+//
+var interface = function(div){
+   mod.div = div
+   //
+   // x
+   //
+   div.appendChild(document.createTextNode('x: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.x = input
+   //
+   // y
+   //
+   div.appendChild(document.createTextNode(' y: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.y = input
+   div.appendChild(document.createElement('br'))
+   //
+   // sides
+   //
+   div.appendChild(document.createTextNode('sides: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.sides = input
+   div.appendChild(document.createElement('br'))
+   //
+   // radius
+   //
+   div.appendChild(document.createTextNode('radius: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 3
+      div.appendChild(input)
+      mod.radius = 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 2a5081e9410b74b3a20a3fc1c412594a212fc8ea..ee75c303268c580cdd9610b915ed45e8e3100697 100644
--- a/modules/index.js
+++ b/modules/index.js
@@ -35,6 +35,7 @@ module_label('   shapes')
 module_label('      2D')
 module_menu('         circle','modules/frep/shapes/2D/circle')
 module_menu('         function','modules/frep/shapes/2D/function')
+module_menu('         polygon','modules/frep/shapes/2D/polygon')
 module_menu('         rectangle','modules/frep/shapes/2D/rectangle')
 module_menu('         right triangle','modules/frep/shapes/2D/right%20triangle')
 module_menu('         triangle','modules/frep/shapes/2D/triangle')