How it works

Crease Lab

An origami robot is a folded sheet plus something that pulls on it. Crease Lab models the sheet as a truss of bars with springy hinges, lets you fold it by hand or let an optimizer pick its shape, teaches a folded tube to crawl with no wheels, and turns the result into a template and servo angles. First time here? The tutorial walks through every bench. After it come the seven ideas it rests on, each with the lines of JavaScript that carry it.

  • Physics simulation
  • Origami mechanics
  • Evolution strategies
  • Locomotion
  • Robot fabrication

From crease pattern to crawling robot

  1. Drawa crease pattern: points, triangles, mountain and valley lines
  2. Foldbars keep panels rigid; hinges pull creases toward their angle
  3. SearchCMA-ES tries shapes and simulates each one
  4. Crawlcables and grippy feet, sequenced into a gait
  5. Buildtemplate, servo angles and Arduino code

The ideas

  1. ▶Tutorial: try every bench
  2. 01A sheet is a truss with springy hinges
  3. 02Folding is a torque on every crease
  4. 03A hand, and paper that remembers
  5. 04Let the optimizer choose the shape
  6. 05Crawling needs an asymmetry
  7. 06Gaits are timed primitives
  8. 07From cable stroke to servo angle
  9. →Results
  10. →Limits
  11. →Reproduce
  12. →Read further
▶

Tutorial

Fifteen minutes through Fold, Move and Build, one thing to try at a time

Open Crease Lab in a second tab and keep this page beside it. The three buttons at the top, Fold, Move and Build, are three workbenches. Settings are on the left, the 3D view is in the middle, and results are on the right. In the 3D view, drag empty space to turn the camera and scroll to zoom. Reset view puts the camera back.

Words you'll see

WordMeaning
Crease patternThe flat drawing of where the paper folds. It's shown on the green cutting mat.
Mountain / valleyThe two ways a crease can fold. A mountain makes a ridge pointing at you (red, dash-dot). A valley makes a trough (blue, dashed).
StrainHow far the paper has to stretch to take a shape. Real paper barely stretches, so a design with high strain will tear or refuse to fold.
KreslingA tube of triangles that twists as you squash it. The crawling robot is a row of them on its side.
GaitA repeating sequence of moves (squeeze, let go, grip, bend) that makes the body travel.

Fold: shapes from flat paper

  1. Collapse a Miura-ori

    In Fold, pick Miura-ori under Pattern. In Actuation, drag Fold amount from 0% up toward 90%, or tick Cycle fold open and closed and let it run.

    You should see the flat sheet buckle into a zigzag and pack down into a narrow stack. The whole sheet moves together: pull any part and the rest follows. That's why this pattern is used to fold solar panels for launch.

  2. Fold it with your hands

    Untick the cycle box. In the 3D view, double-click one corner of the paper to pin it, then drag the opposite corner. To make the paper keep the shape you give it, tick Creases keep the shape you fold by hand first.

    You should see the creases bend as you pull and the panels stay flat. Without the pin, the whole sheet just slides along with your mouse.

  3. Change a crease

    On the Crease pattern mat (top right), click any red or blue line. In the panel that opens, switch it between Mountain, Valley and Flat (no fold), or drag Fold just this crease. Reset folds undoes every change.

    You should see the 3D sheet fold differently straight away. Flip one Miura crease the wrong way and the sheet fights itself: it can't collapse cleanly any more.

  4. See where the paper stretches

    Press Strain map under the 3D view. Fold the Miura-ori, then switch the pattern to Yoshimura and fold that.

    You should see the Miura stay pale, so it folds without stretching. The Yoshimura turns yellow and red, so its panels have to stretch to fold. Real paper would wrinkle or tear there.

  5. Let the computer pick the shape

    Back on Miura-ori, go to Optimize the design. Set Goal to Fold most compactly and press Run search. Each dot is one design it tried and simulated, and the line is the best so far. When it stops, press Use best design.

    You should see the Geometry sliders jump to new values and a sheet that packs smaller than the default. The search is CMA-ES, explained in section 04.

  6. Make a tower that snaps

    Pick Kresling tower. Now the Actuation control is a cable: drag Tendon pull, or press Pulse tendon for a quick tug. Then press Run sweep in Characterize.

    You should see the tower twist as it shortens. The sweep plots how hard it pushes back as it's squashed. If the curve dips below zero, the tower snaps shut and stays shut on its own (it's bistable). Try a different Twist per cell and run the sweep again.

Move: a robot with no wheels

  1. Watch it crawl

    Click Move. A Kresling tube lies on its side with four cables running through it. In Gait, leave Start from on Inchworm.

    You should see the body squeeze and relax over and over while it inches forward. The feet are directional scales: they slide forward easily but grip backward, like the hairs on a ski skin. So each squeeze pulls the tail up and each relax pushes the head on.

  2. Drive it yourself

    In Motion primitives, drag Contract to squeeze, then drag Bend and pick a Bend toward direction. The gait pauses while you do this. Press Back to the gait when you're done.

    You should see that Contract shortens the whole body, while Bend shortens one side so the body curls. Bending is how the robot steers.

  3. Take away the grip

    In Feet, choose Plain paper, then press Score this gait.

    You should see the body stretch and squeeze but go nowhere. The path chart is nearly a dot. With the same friction in every direction, the front and back slip equally and cancel out. Crawling needs something lopsided: scales, or anchors that grip on command (Switchable anchors, used by the Two-anchor crawl gait).

  4. Teach it a better gait

    Put Feet back on Directional scales. In Learn a gait, set Goal to Crawl fastest and press Run search. It tries many timings and squeeze amounts, crawling each one in the simulator. Then press Use learned gait and Score this gait.

    You should see the phases in the Gait card change and a longer line on the path chart. You can also edit a phase by hand, or press Add phase, and score it again.

Build: the real thing

  1. Get a template and code

    Click Build. It uses the robot and gait you set up in Move. Choose your Controller board, then flip between Body strip, Plates and pads and 3D robot in the middle. On the right are the Servo plan, Wiring, Parts list, Controller code and Build steps.

    Press Download build kit (.zip) to get everything in one file.

    You should see a printable crease pattern for the body (score the lines, fold and glue it into a tube), cut-outs for the plates and foot pads, and an Arduino sketch that plays your gait on the servos. The servo section explains how cable lengths become servo angles.

If something looks wrong

The sheet flew off or crumpled. Press Defaults on Geometry, or pick the pattern again to rebuild it. Very low damping or stretch stiffness makes it wobbly.

I can't click the Kresling creases. That's on purpose. The tower's folds come from its geometry, so the mat only shows the strip to cut out.

Nothing moves in Move. Check that Pause isn't pressed, and press Back to the gait if you were driving it by hand.

The numbered sections below explain what the simulator is doing behind each of those buttons.

01

A sheet is a truss with springy hinges

The bar-and-hinge model

Simulating paper as a continuous shell is slow and fussy. The bar-and-hinge model, used by MERLIN (Liu and Paulino) and Origami Simulator (Ghassaei, Demaine and Gershenfeld), keeps only what matters for folding. Every crease pattern is cut into triangles. Each triangle edge becomes a bar that resists stretching, and each edge shared by two triangles becomes a hinge with a rotational spring. Mountain and valley creases get soft hinges; the diagonals that split a flat panel get stiff ones, so the panel stays nearly flat.

Bars are linear springs with a damper along their own direction. The stiffness is axial / L0, so a short bar and a long bar of the same material stretch by the same fraction under the same load.

web/core.jslines 414–423
      for (const b of this.allBars) {
        const i = 3 * b.i, j = 3 * b.j;
        const dx = x[j] - x[i], dy = x[j + 1] - x[i + 1], dz = x[j + 2] - x[i + 2];
        const L = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1e-12;
        const ux = dx / L, uy = dy / L, uz = dz / L;
        const rv = (v[j] - v[i]) * ux + (v[j + 1] - v[i + 1]) * uy + (v[j + 2] - v[i + 2]) * uz;
        const fs = b.k * (L - b.L0) + b.c * rv;
        f[i] += fs * ux; f[i + 1] += fs * uy; f[i + 2] += fs * uz;
        f[j] -= fs * ux; f[j + 1] -= fs * uy; f[j + 2] -= fs * uz;
      }
  1. The bar's unit direction and how fast its two ends move apart along it.
  2. Spring plus damper: k(L − L0) pulls the bar back to its rest length and c·v bleeds off oscillation. The damping ratio is 0.45 of critical.
  3. Equal and opposite forces on the two ends, so bars never push the sheet as a whole.

The hinge needs the fold angle between its two triangles and how that angle changes when any of the four corners moves. The angle comes from the two face normals; the gradient is the closed form from Liu and Paulino (2017). Valley folds are positive, as in the FOLD file format. A test in development compared this gradient with finite differences and they agreed to 10−10.

web/core.jslines 389–408
      const sinT = (cxm * rkjx + cym * rkjy + czm * rkjz) * im * inn / L;
      const cosT = -(mx * nx + my * ny + mz * nz) * im * inn;
      let th = Math.atan2(sinT, cosT);
      // unwrap
      const d = th - h.theta;
      if (d > PI) th -= 2 * PI; else if (d < -PI) th += 2 * PI;
      if (grad) {
        // Liu & Paulino (2017) dihedral gradient; sign matched to `th`
        const a1 = (rijx * rkjx + rijy * rkjy + rijz * rkjz) / LL;
        const a2 = (rklx * rkjx + rkly * rkjy + rklz * rkjz) / LL;
        const s = -1;
        const gi = [s * L / mm * mx, s * L / mm * my, s * L / mm * mz];
        const gl = [-s * L / nn * nx, -s * L / nn * ny, -s * L / nn * nz];
        grad[0] = gi[0]; grad[1] = gi[1]; grad[2] = gi[2];
        grad[3] = gl[0]; grad[4] = gl[1]; grad[5] = gl[2];
        for (let c = 0; c < 3; c++) {
          grad[6 + c] = (a1 - 1) * gi[c] - a2 * gl[c];
          grad[9 + c] = (a2 - 1) * gl[c] - a1 * gi[c];
        }
      }
  1. Signed angle from the two face normals m and n, measured about the hinge axis: 0 when flat, +180° folded fully as a valley.
  2. Unwrap against the previous angle, so a crease folded past 180° does not suddenly jump to −180°.
  3. Moving a wing corner straight off the hinge changes the angle at a rate of 1 / (its distance from the hinge), along the face normal.
  4. The two hinge corners take the rest, weighted by where the wings sit along the hinge, so the four gradients sum to zero and the hinge never pushes the sheet as a whole.
02

Folding is a torque on every crease

Targets, not positions

Nothing in the model says where the paper should end up. Each hinge only has a target angle, and its spring pulls toward that target. The fold slider sets every valley's target to +180° × the fold amount and every mountain's to −180° × the fold amount, the same trick Origami Simulator uses. The bars then decide what shape is possible.

web/core.jslines 365–374
    setFold(p) {
      // drive M/V creases toward +/- pi * p (valley positive, FOLD convention)
      this.foldPct = p;
      const mf = this.opts.maxFold * PI;
      for (const h of this.hinges) {
        if (h.asg === 'V') h.target = h.rest + p * (mf - h.rest);
        else if (h.asg === 'M') h.target = h.rest + p * (-mf - h.rest);
        else h.target = h.rest;
      }
    }
  1. A valley heads for +π·p, a mountain for −π·p. rest is 0 for a flat sheet; for the Kresling tower it is the angle of the built tower, so the tower is stress-free as built.
  2. Flat panel lines aim to stay flat.

The hinge force is the torque −k(θ − θtarget) times the gradient from 01, applied to all four corners. The model steps forward in time with semi-implicit Euler, using a time step set from the stiffest node so it stays stable.

web/core.jslines 425–434
      for (const h of this.hinges) {
        const th = this.hingeAngle(h, g);
        h.theta = th;
        const tq = -h.k * (th - h.target);
        const ids = [h.w1, h.w2, h.h1, h.h2];
        for (let q = 0; q < 4; q++) {
          const n = 3 * ids[q];
          f[n] += tq * g[3 * q]; f[n + 1] += tq * g[3 * q + 1]; f[n + 2] += tq * g[3 * q + 2];
        }
      }
  1. Torque from the rotational spring. h.k is the crease stiffness times the crease length, so long creases are proportionally stiffer.
  2. The same torque times each corner's gradient. This is exactly the force that lowers the spring's energy fastest.
A side effect worth knowing

Asking every crease for the same angle is not quite what a Miura-ori wants: its zigzag creases and straight creases fold at different rates. The creases fight a little, and panels stretch by about 1.3% midway through the fold, then relax as it closes. A real Miura folds with no stretch at all, so read the strain map as "how hard the creases disagree", not as tearing.

03

A hand, and paper that remembers

Direct manipulation in 3D

Dragging the paper casts a ray from the mouse into the scene, finds the triangle it hits and the exact point on it, and attaches a spring from that point to the cursor. The point is a weighted mix of the triangle's three corners, so you can grab the middle of a panel, not just a corner. Double-clicking pins a corner in place, which is what you need to fold one half of a sheet over the other.

web/core.jslines 442–460
      if (this.grab) {
        const g = this.grab, k = g.k || 25, c = 2 * Math.sqrt(k);
        const nodes = g.nodes || [g.node], w = g.w || [1];
        for (let d = 0; d < 3; d++) {
          let pd = 0, vd = 0;
          for (let q = 0; q < nodes.length; q++) { pd += w[q] * x[3 * nodes[q] + d]; vd += w[q] * v[3 * nodes[q] + d]; }
          g._F = g._F || [0, 0, 0];
          g._F[d] = k * (g.target[d] - pd) - c * vd;
        }
        // a hand, not a vice: cap the pull so panels do not get torn apart
        const Fm = Math.hypot(g._F[0], g._F[1], g._F[2]), cap = g.maxForce || 2;
        const sc = Fm > cap ? cap / Fm : 1;
        for (let d = 0; d < 3; d++) for (let q = 0; q < nodes.length; q++) f[3 * nodes[q] + d] += w[q] * g._F[d] * sc;
      }
    }
    // paper memory: creases keep the angle they were bent to
    setCreasesToCurrent() {
      for (const h of this.hinges) if (h.asg === 'M' || h.asg === 'V') h.target = h.theta;
    }
  1. A spring and damper between the grabbed point and the cursor. Velocities are mixed with the same barycentric weights as positions.
  2. A hand, not a vice: the pull is capped. Without the cap, dragging fast stretches the bars by 10% or more and the sheet looks torn.
  3. Letting go copies each crease's current angle into its target, so the fold stays where you left it, like creased paper. You can switch this off on the page.

Clicking a crease on the cutting mat selects it. You can flip it between mountain, valley and flat, or hold just that crease at an angle while everything else follows the fold slider.

05

Crawling needs an asymmetry

Gravity, a floor, friction and cables

The robot is a Kresling tube lying on its side: rigid plates between the cells, a foot pad under each end plate, and four cables running the whole length through an eyelet in every plate. Cables are internal forces, so pulling them can never move the robot's centre of mass. Only the ground can. That is the whole problem of locomotion without wheels: a body that stretches and shrinks symmetrically on uniform friction goes nowhere, and the page's "plain paper" gait shows exactly 0% progress per cycle.

Something has to be asymmetric. With directional feet (like the hairs on a climbing skin), the contacts slide forward easily and grip backwards. With switchable anchors, the robot chooses which end grips in each phase.

web/motion.jslines 246–258
        // Coulomb friction as a velocity-level impulse, split along / across the body axis
        const ax = this.axisVector(); let hx = ax.dx, hy = ax.dy; const hl = Math.hypot(hx, hy) || 1; hx /= hl; hy /= hl;
        const lx = -hy, ly = hx;
        for (let i = 0; i < N; i++) {
          const nrm = this.normal[i]; if (!nrm) continue;
          const p = 3 * i;
          let va = v[p] * hx + v[p + 1] * hy, vl = v[p] * lx + v[p + 1] * ly;
          const mua = va > 0 ? this.muF[i] : this.muB[i];
          const da = mua * nrm * dt, dl = this.muS[i] * nrm * dt;
          va = Math.abs(va) <= da ? 0 : va - Math.sign(va) * da;
          vl = Math.abs(vl) <= dl ? 0 : vl - Math.sign(vl) * dl;
          v[p] = va * hx + vl * lx; v[p + 1] = va * hy + vl * ly;
        }
  1. Each contact's velocity is split into a part along the body and a part across it. Directional feet use a low coefficient forwards (0.12) and a high one backwards (1.2).
  2. Coulomb friction as an impulse: friction removes at most μ·N·dt of sliding speed per step and never reverses it. So a foot either sticks completely or slides, and the step stays stable even with an anchor's μ of 40.

A cable through eyelets pulls each pair of neighbouring eyelets together with its tension. The eyelets sit on the plates, between the corners and the centre, so the pull goes into the plates rather than straight along a crease, where a stiff bar would simply absorb it.

web/motion.jslines 213–229
      // cables through eyelets: each straight run pulls its two ends together
      const cab = this.mesh.pat.cables;
      for (let c = 0; c < cab.length; c++) {
        const T = this.cableT[c]; if (!T) continue;
        const eyes = cab[c].eyes;
        let pa = this.eyelet(eyes[0]);
        for (let q = 1; q < eyes.length; q++) {
          const pb = this.eyelet(eyes[q]);
          const dx = pb[0] - pa[0], dy = pb[1] - pa[1], dz = pb[2] - pa[2];
          const L = Math.hypot(dx, dy, dz) || 1e-9;
          const fx = (T * dx) / L, fy = (T * dy) / L, fz = (T * dz) / L;
          const A = eyes[q - 1], B = eyes[q];
          for (let r = 0; r < A.nodes.length; r++) { const i = 3 * A.nodes[r], w = A.w[r]; f[i] += w * fx; f[i + 1] += w * fy; f[i + 2] += w * fz; }
          for (let r = 0; r < B.nodes.length; r++) { const i = 3 * B.nodes[r], w = B.w[r]; f[i] -= w * fx; f[i + 1] -= w * fy; f[i + 2] -= w * fz; }
          pa = pb;
        }
      }
  1. Tension along the straight run between two eyelets.
  2. Each eyelet is a fixed weighted mix of its plate's corners, so the force is shared among them with the same weights.
What it took to get this right

The first version routed cables along the tube's own creases, and pulling them did almost nothing. The next let the end plates rest on their corners, and a one-sided pull just tipped them over. The model only behaved once each plate was made a thick rigid disc (two hub points just off its plane), each end got a foot pad with four contact points, and gravity was heavy enough to keep the pads down.

06

Gaits are timed primitives

Contract, bend, twist, anchor, release

A gait is a short list of phases. Each phase sets a cable pattern and which feet grip. Contract pulls all four cables; bend adds extra pull on one side; twist comes for free when every cell has the same handedness; release just lets the creases spring the body back. Between phases the tensions ease over a third of a second, like a servo moving.

web/motion.jslines 335–358
  function cablePattern(sim, ph, Tmax) {
    return sim.cableAngle.map((a) => Tmax * Math.max(0, ph.contract + ph.bend * Math.cos(a - ph.dir * DEG)));
  }

  class GaitPlayer {
    constructor(gait) { this.gait = gait; this.t = 0; this.prev = null; this.idx = -1; }
    period() { return this.gait.phases.reduce((s, p) => s + p.dur, 0); }
    phaseAt(t) {
      const P = this.period(); let u = ((t % P) + P) % P;
      for (let i = 0; i < this.gait.phases.length; i++) { const d = this.gait.phases[i].dur; if (u < d) return { i, u, d }; u -= d; }
      return { i: this.gait.phases.length - 1, u: 0, d: 1 };
    }
    apply(sim, t) {
      const g = this.gait, ph = this.phaseAt(t), cur = g.phases[ph.i];
      const prev = g.phases[(ph.i - 1 + g.phases.length) % g.phases.length];
      const Tmax = (g.tension || 1) * sim.tensionScale;
      const a = cablePattern(sim, prev, Tmax), b = cablePattern(sim, cur, Tmax);
      const ramp = Math.min(0.5, 0.35 * ph.d), w = Math.min(1, ph.u / ramp);
      const s = w * w * (3 - 2 * w);
      for (let c = 0; c < a.length; c++) sim.cableT[c] = a[c] + (b[c] - a[c]) * s;
      if (g.feet !== sim.feet || ph.i !== this.idx) sim.setFeet(g.feet, { front: cur.front || false, rear: cur.rear || false });
      this.idx = ph.i;
      return ph;
    }
  1. Cable tension is a contraction everyone shares plus a cosine-shaped extra pull toward the bend direction.
  2. A smoothstep blend from the previous phase's tensions to this phase's.
  3. Anchors switch at the start of a phase, all at once.

The same CMA-ES from 04 can tune a gait: it keeps the order of phases and their anchors, and searches each phase's duration, contraction and bend. Every candidate is crawled for three cycles (one warm-up, two scored), about 20 ms each.

07

From cable stroke to servo angle

What a real robot needs from the simulator

Hobby servos hold an angle, not a force. So the one number the build needs from the simulator is how far each cable pair has to shorten in each phase. The Build tab pulls each phase's cable pattern until the body settles, measures the cable paths, scales them to the chosen size and divides by the spool radius.

web/build.jslines 193–213
  function servoPlan(params, gait, opts) {
    opts = Object.assign({ R_mm: 30, spool_mm: 10, servoRange: 180, stiff: null }, opts || {});
    const phases = gait.phases.map((ph) => {
      const sim = CL.buildRobot(params, opts.stiff);
      sim.tensionScale = CL.tensionScaleFor(sim);
      const Tmax = (gait.tension || 1) * sim.tensionScale;
      sim.cableAngle.forEach((a, c) => { sim.cableT[c] = Tmax * Math.max(0, ph.contract + ph.bend * Math.cos(a - ph.dir * DEG)); });
      sim.setFeet('plain');
      sim.step(Math.round(4 / sim.dt));
      const short = sim.mesh.pat.cables.map((c, i) => Math.max(0, sim.cableRest[i] - sim.pathLength(c)));
      const leftMm = ((short[0] + short[3]) / 2) * opts.R_mm, rightMm = ((short[1] + short[2]) / 2) * opts.R_mm;
      return { dur: ph.dur, leftMm, rightMm, head: ph.front || false, tail: ph.rear || false };
    });
    const maxMm = Math.max(1e-6, ...phases.map((p) => Math.max(p.leftMm, p.rightMm)));
    const deg = (mm) => (mm / opts.spool_mm) / DEG;
    for (const p of phases) { p.leftDeg = deg(p.leftMm); p.rightDeg = deg(p.rightMm); }
    const maxDeg = deg(maxMm);
    const usable = opts.servoRange * 0.85;
    const spoolNeeded = maxMm / (usable * DEG);
    return { phases, maxMm, maxDeg, fits: maxDeg <= usable, spoolNeeded, spool_mm: opts.spool_mm, R_mm: opts.R_mm, anchors: gait.feet === 'anchors', feet: gait.feet };
  }
  1. Shortening of each cable, averaged into a left pair and a right pair (one servo each), in millimetres at the chosen plate size.
  2. Arc length to angle: θ = stroke / spool radius.
  3. The spool that keeps the largest stroke inside 85% of a 180° servo. For the default robot at 30 mm that is a 22 mm radius: the 59 mm stroke becomes 150°.

The same tab draws the fold template at true scale (the flat strip comes from unrolling the tower's triangles, whose three side lengths fix the strip's slant), the plates with their eyelet holes, a wiring diagram and parts list, and an Arduino sketch whose phase table holds these angles and timings. Everything downloads as one zip.

→

Results

Default designs, measured in the simulator

Units are the model's own. Lengths are in pattern units (1 = one plate radius for the crawler) and forces are relative to the bar stiffness, so compare designs with each other rather than reading the numbers as newtons.

Folding

PatternFootprint at 95% foldPeak panel strainat fold
Miura-ori 4×44.8%1.33%50%
Yoshimura 5×41.8%0.62%55%
Waterbomb base9.7%0.46%30%

The default Kresling tower is bistable: pushing its lid down takes a peak force of 2.20 at 9% compression, the force then turns negative (down to −0.47), and the tower comes to rest again near 72% compression.

Design search (12 generations, seed 1)

  • Miura, fold most compactly at 60%: score 0.49 → 0.71. The footprint left at 60% fold went from 44% to 15%, at the cost of strain rising from 1.2% to 2.3%. The search pushed the sector angle to its 85° limit and the zigzag edge to its 0.4 limit, so the answer is "as close to a plain accordion as allowed".
  • Kresling, strongest second stable state: the pull that holds it shut went from 0.47 to 2.12, with a taller cell (1.74) and the twist at its 10° limit.

Crawling (default crawler, cables at 80% pull, three cycles)

GaitFeetForward per cycleTurn per cycleCost of transport
Inchwormdirectional19.8% of body0.0°1.49
Two-anchor crawlanchors9.7%−1.0°3.33
Turn leftanchors7.5%+1.9°2.62
Turn rightanchors6.5%−2.5°3.27
Stretch on plain paperplain0.0%0.0°–

Tuning the inchworm with CMA-ES for 10 generations (90 gaits) raised its two-cycle score from 18.7% to 20.5% of a body length per cycle, mostly by lengthening both phases. The gait was already close to the best this body allows.

→

Limits

Honest caveats

  • Nothing here has been checked against a real fold or a real robot yet. Crease stiffness in particular is a guess; the page tells you to measure the built body's contraction and tune the simulator to match.
  • The units are made up. Gravity, friction and stiffness were tuned by hand until the crawler behaved plausibly, so absolute speeds and forces mean little.
  • Driving every crease toward the same angle overstates Miura strain (02).
  • Steering is weak: about 2° per cycle, and the two-anchor crawl drifts about 1° right per cycle, apparently from how the alternating cells twist.
  • There is no self-collision. Panels can pass through each other past about 60% contraction, and the hand can push the sheet through itself.
  • The optimizer often ends at the edge of a parameter's range, which means the range, not the physics, is choosing the answer there.
  • The generated Arduino sketch has only been syntax-checked, not compiled for a board or run on hardware.
  • It is plain JavaScript on one thread: fine for exploring, too slow for serious reinforcement learning. CreaseLab.CrawlerEnv has the reset/step shape you would port to JAX or PyTorch.
→

Reproduce

No build step, no install

Open the page. Everything is exposed as window.CreaseLab, so the numbers above can be rerun from the browser console:

const CL = window.CreaseLab;
CL.evaluateGait(CL.defaultRobot(), CL.GAITS.inchworm, { cycles: 3 });   // perCycle 0.198
const s = CL.buildMesh(CL.buildPattern('miura', CL.defaultParams('miura')));
s.damping = 2; CL.foldSweep(s, 19, 0.95);                              // footprint and strain vs fold
const env = new CL.CrawlerEnv({}, { feet: 'anchors' });                // reset() / step(action)

The same files also load in Node (require('./web/build.js') pulls in the rest), which is how the tables here were made.

→

Read further

Where everything lives

  • web/core.js crease patterns, the bar-and-hinge simulator, hand grabbing, FOLD import and export, CMA-ES
  • web/motion.js the crawler body, ground and friction, cables, primitives, gaits, gait search, CrawlerEnv
  • web/build.js fold templates, servo plan, parts list, Arduino sketch, README and zip writer
  • web/app.js the page: 3D view (three.js r128, vendored), the three tabs