vibespire.ai / Paper Dolls /

how it's made

open the terrarium β†—

Vibespire presents

How the Paper Dolls Are Made

There is no skeleton in this terrarium, no keyframe and no walk cycle. Each creature is a handful of circles told to stay certain distances apart, with a drawing laid over them and four feet that decide for themselves where to stand. About a thousand lines, no engine.

Both figures on this page run the engine's own classes.

The rig

Ten points, some distances, a doll on top

The technique comes from a 2016 GDC talk in which Rain World's creator describes his whole animation system in one sentence: β€œI have a bunch of points in space and I connect them at certain distances… they act as a frame.”

A point is a Chunk: a circle with a position, a previous position and a radius. Velocity is not stored β€” it is the gap between those two positions, which is what makes the integration Verlet. A connection is a DistanceConstraint: a rest length and a stiffness, solved by pushing both ends along the line between them until the gap is right, weighted by inverse mass. Stiffness 1 is a bone; anything less is connective tissue.

That is the entire rig. The figure beside this is makeRope β€” ten chunks at radius 4.5, linked at 10 units, stiffness 1 β€” dropped into a small room and solved by the same Solver the creatures use. Drag it about: rope, for free, from a class with no concept of rope.

Then slide the skin up. The doll layer is drawn over the frame and knows nothing about it beyond where the circles ended up: a chain of capsules along the spine with a width profile tapering to the tail. Painter's order per creature is far limbs, then body and tail as one silhouette, then the head, then near limbs, with the far side drawn in a lighter grey so it reads as behind.

The drawn radii and the physical radii are different numbers on purpose. A lizard's tail simulates at radius 2 all the way down and is drawn from 5.4 tapering to 0.8 β€” the silhouette is a costume, and costumes do not have to weigh anything.

The engine's own makeRope, Solver and chainShape, imported by this page.

The solve

Four passes, in an order that matters

Every tick, the whole world is integrated once and then relaxed four times. The order inside a pass is the part worth knowing:

for (let pass = 0; pass < SOLVER_PASSES; pass++) {
  for (const b of bodies) for (const dc of b.constraints) dc.solve();
  for (const b of bodies) for (const pin of b.pins) pin.solve();
  for (const b of bodies) for (const c of b.chunks) collideChunk(c, terrain);
}

Constraints first, so the frame is roughly itself. Pins next β€” the drag grab and the pole cling β€” so a deliberate hold beats the frame's own preferences. Collision last, so a constraint can never leave a chunk embedded in the floor for more than an instant. Four passes is enough to look rigid and cheap enough to run for every body in the room at 40 Hz.

Collision is positional and inelastic: push the circle out along the surface normal, then rewrite the implicit velocity β€” cancel any component approaching the surface, keep any separating one, and multiply the tangential part by 0.93. Four passes of that is about 0.75 retained per tick, which is the friction.

Two details are pure defence. The side of a surface a chunk is pushed to is decided by where it was on the previous tick, so something that crossed a plank mid-tick pops back out the way it came instead of being ejected through the far side. And every chunk's per-tick displacement is capped at 9 world units, which is what stops a lizard's lunge from tunnelling through a wall.

One-way planks are ghosts from below: if the chunk's previous position was on the wrong side of the normal, the collision is skipped entirely. Nothing can wedge itself under a shelf, because from underneath the shelf does not exist.

Hands on

Try it

This is the terrarium β€” the same level, solver, creatures and brains the demo builds. Turn on the x-ray to strip the dolls off, then pick the lizard up by the tail. Nobody keyframed its indignation.

show

The actual Solver, Scrambler, Lizard and their brains, imported by this page.

Legs

A foot is not physics

There is no walk cycle, and there is also no leg. A limb is a point with a four-state machine and some cosmetic geometry at draw time.

A planted foot sits at a fixed world position and stays there while the body walks over it. Each tick it asks two questions: am I stretched past 96% of my reach, and am I further than my comfort radius from where I would ideally be β€” a point lead units ahead of the shoulder in the direction of travel, dropped onto whatever surface is below it. The lizard's comfort radius is 20 units and its lead 18; the scrambler's are 15 and 13, which is most of why one prowls and the other potters.

If either answer is yes, the foot asks the gait coordinator for permission, samples the terrain for candidate footholds within 1.7 comfort radii, discards any that are out of reach or on the wrong side of a surface β€” you cannot plant on an underside β€” and takes the nearest to ideal. Then it swings: an eased interpolation with a lift added along the destination surface's own normal, so a step onto the floor arcs upward and a step onto a pole arcs outward, from one line of code.

Mid-swing, if the body has run away and the target is now past 1.25 reaches, the foot retargets β€” or gives up and starts dangling, hanging under the shoulder with a small sine sway while it keeps probing for footing. A planted foot stretched past 1.12 reaches lets go rather than rubber-banding.

The gait rule is one method: a limb may only begin a swing when no limb of another group is mid-swing, or has been planted for less than 0.05 seconds. Both creatures assign their four limbs into two diagonal pairs. Nothing in the code names a trot; a trot is what that rule produces.

Only at draw time does anything resembling a leg appear: analytic two-bone IK by the law of cosines places a knee between shoulder and foot, with a sign choosing which of the two mirror solutions to use. The limb never touches the frame, which is the whole reason the rig cannot be broken β€” a foot has no way to destabilise the body it hangs from.

Turn the x-ray on above and slow time. The Γ— is the chosen foothold, the dashed circle the comfort radius. Shove a creature and watch a swing retarget in flight.

Locomotion

Brains push the body, not the legs

Nothing here moves by animation, and nothing moves by walking. A brain writes two numbers β€” a direction and a target speed β€” and the body is pushed.

A ride-height spring holds the creature up. For each body chunk it finds the ground below, computes the error against the standing height, and applies βˆ’gravity + stiffness Γ— error βˆ’ damp Γ— velocity, scaled by how supported the creature is: min(1, plantedFeet / 2). Lift both feet and the support term goes to zero and the creature falls, because standing is something the feet are permitting rather than something the body assumes.

The horizontal drive is a plain acceleration toward the goal β€” 260 units/sΒ² for the lizard, 330 for the lighter scrambler β€” applied only while the chunk is still slower than the brain's target speed, and cut to 15% authority when nothing is planted. That last number is why a creature in mid-air can nudge itself but cannot fly.

Every gait difference in the terrarium comes out of that arrangement rather than out of a different animation. Stalking is one number: crouch rises to 1 over half a second and scales the ride height down by 30%, with an extra 40% off for the lizard's head chunk specifically. The classic pre-lunge posture is two multipliers. The lunge itself is a velocity kick of 7.2 units per tick plus a raised speed cap for 0.42 seconds.

Because the body is pushed and the terrain does the rest, the same code walks a flat floor, a gully and a one-way plank without knowing which it is on.

The rest of the animal

Tails are physics, heads are theatre

The three remaining parts of a creature are each solved a different way, and the differences are deliberate.

A tail is real physics β€” a chain of chunks at a quarter mass hanging off the hips, linked at stiffness 0.8, plus weak skip-links at stiffness 0.06 between every other chunk for a hint of muscle tone. It drapes over ledges, trails through turns and whips when the creature is thrown, and none of that is written anywhere. The only cheat is a small upward acceleration on the first two links while the creature is standing, so the base rides aloft instead of dragging.

A head is not physics at all. It is one angle lagging toward whatever seems interesting β€” the cursor if it is near, a threat, otherwise the direction of travel β€” at a rate of min(1, 7 Γ— dt) per tick, through an angle lerp that goes the short way round. The scrambler will watch your pointer if you bring it within 170 units.

Dragging is one constraint. Grab a creature and a PinConstraint at stiffness 0.4 is pushed onto its pin list, pulling one chunk toward the cursor each pass. Nothing else changes. The body dangles because dangling is what those rules were always going to do to a frame held at one point, and it picks itself up afterward because its brain has a righting state that waits for 0.3 seconds of continuous ground contact before standing up again.

Being dragged is not a special case in the physics. It is a special case in the brain, which is a much smaller thing to get wrong.

HOW EACH PART IS SOLVED spine rigid distance constraints tail springy chain, quarter mass limb state machine, then IK at draw head one angle, lagging its target
Four parts, four solutions. Only two of them are simulated.

The room

One authored room and two little lives

The terrarium is eleven segments β€” walls, a floor with a gully in it, a ceiling, two one-way planks and two poles β€” and eight waypoints joined by nine edges.

Terrain queries are brute force over all of them. At this size a spatial index would cost more in code than it saves in time, and saying so in a comment is cheaper than maintaining one. Poles are the odd segment out: walkable but non-colliding, so a foot can plant on one while a body passes straight through.

The waypoint edges carry a kind β€” walk, pole or drop β€” and pole and drop edges are one-way. That is the whole ecosystem. The lizard's path search is told it cannot use pole edges, so the refuge plank at the top of the right-hand pole is a node it can reach on no path at all. The scrambler owns the air; the lizard owns the floor.

Each brain is a small state machine over that graph. The lizard patrols, pauses, spots prey within 260 units if the line of sight is clear, sinks into a stalk for 1.5 to 3 seconds, commits to one lunge, and then loses interest for eight seconds β€” a cooldown in which it ignores the scrambler entirely and wanders off. If the prey keeps its distance for three seconds past the stalk timer, it pretends it never cared.

The scrambler potters, looks at things, climbs poles for fun and flees to the refuge when a predator comes within 190 units β€” 300 if that predator is already crouching, because a stalking posture is more alarming than a strolling one. Safe on the plank, it stops and watches until the threat is gone and a three-to-six second nerve timer runs out.

Neither brain has a rule about the other's abilities. The drama comes from one flag on nine edges.

Time

A 40 Hz heart and a 13 fps homage

The simulation runs at a fixed 40 ticks a second, accumulating real elapsed time and stepping until the debt is paid. Rendering then interpolates between the last two ticks, so the motion is smooth at any refresh rate and the physics is identical on every machine.

Every chunk carries two extra position slots for exactly this: the tick before last, and the last. Drawing at an alpha between them costs one lerp and removes the entire class of bug where a heavier frame makes a creature behave differently.

Rain World went the other way. It embraced its low tick rate and drew crunchy, decisive frames, and the switch above turns that on here β€” draw every third tick with no interpolation at all, through an offscreen canvas at a third of the resolution, blitted back with smoothing off. 40 Γ· 3 is about 13.3 frames a second.

It is a worse renderer by every measure and it makes the creatures look more alive, because a pose held for three ticks reads as a decision.

The bill

What this approach costs

Circles, distances and a state machine buy creatures that cope with anything and give up almost all authorial control. Some of that is the point and some of it is just the cost.

You cannot pose anything. There is nowhere in this codebase to say where a hand should be on a given frame. Every silhouette is whatever the solver arrived at, and the same instruction produces a slightly different picture every time it is followed.

The world is authored by hand, in numbers. Eleven segments and eight waypoints typed into a file, with the refuge node's index written into the level. There is no editor and no import; a new room means editing coordinates and re-checking that every foothold still works.

Feet do not know what they are standing on. A foothold is the nearest sampled point on a walkable segment, scored on distance alone. Nothing checks whether the surface is sensible for the creature's size, and on much rougher terrain than this the scoring would need to grow a real cost function.

Positional constraints are not a physics engine. Four relaxation passes give a frame that looks rigid; push hard enough and it stretches, and the 9-unit step cap is a blunt guard rather than continuous collision detection. It holds because everything here is small, light and slow.

The brains are state machines with timers, not planners. The lizard's disinterest is a number, not a decision. It works because eight seconds of indifference reads as a mood, and it would not survive a bigger cast.

The technique is not mine: it is Joar Jakobsson and James Primate's, from The Rain World Animation Process, and a lovely gateway to it is argonaut's procedural animation video.

Nothing in here stores a movement, so nothing in here can repeat one. In exchange, every creature copes with a room it has never been shown.

Open the terrarium β†—

Marcin β€” creator of vibespire.ai

About

Hi, I'm Marcin.

I build these experiments whenever something sparks my curiosity β€” a paper, a game, the way grass bends in the wind, the smallest everyday things. Inspiration shows up, and I chase it into a little living world you can open in a browser.

vibespire is where those experiments live. Poke at them, break them, read how they're made β€” and if something sparks an idea for you too, say hello.