diff --git a/files.html b/files.html
index d8a656fc940992ee5c7a6f009055c1ed9a541fd0..86706ed52d5aaf1d1fbd35cfd2f46ea9e1de9036 100644
--- a/files.html
+++ b/files.html
@@ -19,7 +19,6 @@
 &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>
@@ -83,7 +82,8 @@
 &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/transforms/extrude'>extrude</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;<a href='./modules/frep/transforms/inflate'>inflate</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;<a href='./modules/frep/transforms/reflect'>reflect</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;<a href='./modules/frep/transforms/repeat'>repeat</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;<a href='./modules/frep/transforms/repeat%20linear'>repeat linear</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;<a href='./modules/frep/transforms/repeat%20rotary'>repeat rotary</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;<a href='./modules/frep/transforms/revolve'>revolve</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;<a href='./modules/frep/transforms/rotate'>rotate</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;<a href='./modules/frep/transforms/scale'>scale</a><br>
diff --git a/modules/frep/transforms/repeat b/modules/frep/transforms/repeat linear
similarity index 98%
rename from modules/frep/transforms/repeat
rename to modules/frep/transforms/repeat linear
index 695ed4a2f1541d80e99cdcff97ddf3a3e997e3e7..c1edbe2a8c1b6b734cc9bd758b8bcbd6985d7f48 100755
--- a/modules/frep/transforms/repeat
+++ b/modules/frep/transforms/repeat linear	
@@ -1,5 +1,5 @@
 //
-// frep repeat
+// frep repeat linear
 //
 // Neil Gershenfeld
 // (c) Massachusetts Institute of Technology 2018
@@ -20,7 +20,7 @@ var mod = {}
 //
 // name
 //
-var name = 'frep repeat'
+var name = 'frep repeat linear'
 //
 // initialization
 //
diff --git a/modules/frep/transforms/repeat rotary b/modules/frep/transforms/repeat rotary
new file mode 100755
index 0000000000000000000000000000000000000000..338f98833d7b5321f20a22a80aabd2f8d90c6bba
--- /dev/null
+++ b/modules/frep/transforms/repeat rotary	
@@ -0,0 +1,162 @@
+//
+// frep repeat rotary
+//
+// 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 repeat rotary'
+//
+// initialization
+//
+var init = function() {
+   mod.x.checked = true
+   mod.spacing.value = 0
+   mod.number.value = 10
+   }
+//
+// inputs
+//
+var inputs = {
+   shape:{type:'',
+      event:function(evt){
+         mod.shape = evt.detail
+         outputs.shape.event()
+         }}}
+//
+// outputs
+//
+/*
+   (x-x0)%spacing
+   x1 = min + num*space
+   x1-X
+*/
+var outputs = {
+   shape:{type:'',
+      event:function(){
+         var fn = mod.shape.function
+         var variables = mod.shape.variables
+         var type = mod.shape.type        
+         var spacing = parseFloat(mod.spacing.value)
+         var number = parseInt(mod.number.value)
+         var limits = []
+         for (var v = 0; v < mod.shape.limits.length; ++v) {
+            limits[v] = []
+            limits[v][0] = mod.shape.limits[v][0]
+            limits[v][1] = mod.shape.limits[v][1]
+            }
+         if (mod.x.checked) {
+            var xvar = variables[0]
+            var xmin = limits[0][0]
+            var xmax = limits[0][1]
+            var re = new RegExp(xvar,'g')
+            fn = fn.replace(re,`((${xmin})+(${xvar}-(${xmin}))%(${spacing+xmax-xmin}))`)
+            fn = `Math.min(${xvar}-(${xmin}),${fn})`
+            fn = `Math.min((${xmin+number*(spacing+xmax-xmin)})-${xvar},${fn})`
+            limits[0][1] = xmin+number*(xmax-xmin)+(number-1)*spacing
+            }
+         else if (mod.y.checked) {
+            var yvar = variables[1]
+            var ymin = limits[1][0]
+            var ymax = limits[1][1]
+            var re = new RegExp(yvar,'g')
+            fn = fn.replace(re,`((${ymin})+(${yvar}-(${ymin}))%(${spacing+ymax-ymin}))`)
+            fn = `Math.min(${yvar}-(${ymin}),${fn})`
+            fn = `Math.min((${ymin+number*(spacing+ymax-ymin)})-${yvar},${fn})`
+            limits[1][1] = ymin+number*(ymax-ymin)+(number-1)*spacing
+            }
+         else if (mod.z.checked) {
+            var zvar = variables[2]
+            var zmin = limits[2][0]
+            var zmax = limits[2][1]
+            var re = new RegExp(zvar,'g')
+            fn = fn.replace(re,`((${zmin})+(${zvar}-(${zmin}))%(${spacing+zmax-zmin}))`)
+            fn = `Math.min(${zvar}-(${zmin}),${fn})`
+            fn = `Math.min((${zmin+number*(spacing+zmax-zmin)})-${zvar},${fn})`
+            limits[2][1] = zmin+number*(zmax-zmin)+(number-1)*spacing
+            }
+         var shape = {function:fn,variables:variables,limits:limits,type:type}
+         mods.output(mod,'shape',shape)}
+         }}
+//
+// interface
+//
+var interface = function(div){
+   mod.div = div
+   //
+   // axis
+   //
+   div.appendChild(document.createTextNode('axis: '))
+   div.appendChild(document.createTextNode('x'))
+   var input = document.createElement('input')
+      input.type = 'radio'
+      input.name = mod.div.id+'axis'
+      input.id = mod.div.id+'x'
+      div.appendChild(input)
+      mod.x = input
+   div.appendChild(document.createTextNode(' y'))
+   var input = document.createElement('input')
+      input.type = 'radio'
+      input.name = mod.div.id+'axis'
+      input.id = mod.div.id+'y'
+      div.appendChild(input)
+      mod.y = input
+   div.appendChild(document.createTextNode(' z'))
+   var input = document.createElement('input')
+      input.type = 'radio'
+      input.name = mod.div.id+'axis'
+      input.id = mod.div.id+'z'
+      div.appendChild(input)
+      mod.z = input
+   div.appendChild(document.createElement('br'))
+   //
+   // spacing
+   //
+   div.appendChild(document.createTextNode('spacing: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 6
+      div.appendChild(input)
+      mod.spacing = input
+   div.appendChild(document.createElement('br'))
+   //
+   // number 
+   //
+   div.appendChild(document.createTextNode('number: '))
+   var input = document.createElement('input')
+      input.type = 'text'
+      input.size = 6
+      div.appendChild(input)
+      mod.number = input
+   div.appendChild(document.createElement('br'))
+   }
+//
+// 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 8eb59cf8b932ebbcc849311767275b3fd410b2c2..028c637aecc835b240cc845baf99df5361c74d25 100644
--- a/modules/index.js
+++ b/modules/index.js
@@ -53,7 +53,8 @@ module_label('   transforms')
 module_menu('      extrude','modules/frep/transforms/extrude')
 module_menu('      inflate','modules/frep/transforms/inflate')
 module_menu('      reflect','modules/frep/transforms/reflect')
-module_menu('      repeat','modules/frep/transforms/repeat')
+module_menu('      repeat linear','modules/frep/transforms/repeat%20linear')
+module_menu('      repeat rotary','modules/frep/transforms/repeat%20rotary')
 module_menu('      revolve','modules/frep/transforms/revolve')
 module_menu('      rotate','modules/frep/transforms/rotate')
 module_menu('      scale','modules/frep/transforms/scale')