SplasheryPlayground

Definitions, derivations, notes, and long tables start folded.

Chapter 6Moving things

Chapters 1 to 5 moved splats with programs: a recipe's drive sets a part's angle every frame, and the Splash plays out. This chapter is about motion that comes from your finger and from physics: pick a Splash up, throw it, let it land and settle, open its lid, pull a sword out of a stone. The same rule from the effect-quality notes holds throughout: parts move as solid pieces. Nothing is bent. A lid swings on a hinge, a wedge slides out of its orange, and the splats that belong to a part travel with it. This is not behind the labs switch: the ✋ switch is in the Playground for everyone.

Hands-on play: pick up, toss, settle

What it is. With the ✋ switch on, a drag picks up whatever is under your finger. A quick let-go tosses it; it falls, bounces, and comes to rest on the floor under the Splash, inside an invisible play area. The ↺ button sends everything home. The Hands-on button is in the stage's column (Using Splashery shows where), and the H key switches it on a computer. A press has to move 7 screen pixels before it counts as a drag. On a whole Splash, a drag shorter than 24 pixels only nudges it (a Splash made of pieces, joints, or soft parts is picked up at 7 pixels), and a throw needs at least 24 pixels of travel, so a tap or a light touch never throws anything.

How Splashery does it. Splashery has a small physics engine of its own, with no library (src/physics/world.js). It is extended position-based dynamics [11, 12]: each step first predicts where the bodies go from their velocities and gravity, then moves them to where they would be if nothing overlapped and every joint held, then works out velocities from how far they moved. The world is built on the first pick-up, so opening a Splash costs nothing. It steps 1/60 of a second at a time whatever the frame rate, in 8 smaller substeps for a whole Splash and 10 for a Splash with pieces, and it falls asleep once everything has been slower than a small speed for half a second.

Most Splashes play as one rigid body: the stage applies their pose to the whole Splash, so the splats are sorted and taps land where the Splash lies now. A rigid body needs a shape to collide with, and a Splash has only splats, so Splashery finds the outside from them. It samples up to 6,000 of the Splash's splat centers and, in each of 64 directions, keeps the splat that lies furthest out (ignoring the outermost half of a percent, which are usually stray specks). Those 64 points are the body's collision points. A ball gets a sphere instead, so it rolls. The floor is under the Splash; four invisible walls sit just past its widest reach; a ceiling sits above the highest point. Gravity is 26 Splash radii per second squared, so a Splash falls the same way whatever its size on screen. A throw takes its speed from the last tenth of a second of finger motion, and its base speed is capped at 4 Splash radii per second (the Splash's material then scales it, so a baseball or a paper plane can reach 5).

Soft Splashes squish when they land. How much is a number per Splash (61 Splashes have one, such as the Jelly at 1, the Bouncy Ball at 0.55, and the Strawberry at 0.15), and it reaches the screen as two uniform values that squash and bulge the Splash as a whole. This is a squash of the pose, not a bend of the scan: the splats stay where they are inside the Splash's own frame.

A red yo-yo with a white target pattern

A rigid body: the yo-yo

A red octopus with eight curled arms

Arms that ride the body: the octopus

Both Splashes can be picked up and thrown in the Playground.
Show the table: the numbers of hands-on play
Number Where What it does
7 px PRESS_MOVE How far a press moves before it is a pick-up.
24 px NUDGE A drag shorter than this only pushes a whole Splash (pieces, joints, and soft parts are picked up at 7 px). A throw needs at least this much travel.
26 radii/s² GRAVITY Gravity, in Splash radii, so every Splash falls alike.
0.1 s THROW_WINDOW The span of finger motion a let-go takes its speed from.
4 radii/s THROW_MAX The base cap on a throw, before the material scales it (up to 5 radii/s). Slower than 0.4 (THROW_MIN) is a still hold.
0.6 per s AIR_DRAG Air drag on a thrown Splash that has no material of its own.
1/60 s STEP The world's step, whatever the frame rate.
0.45 s HOME_SECS How long ↺ takes to glide everything home.
64 directions hull() A Splash's collision points: the farthest of up to 6,000 sampled splats per direction.
Why Splash radii, and why a speed limit?

Our choice Nothing in the format forces these.

Splashery's Splashes are an inch tall or a planet wide, and the page has no real size. So the world measures everything in the Splash's own radius: gravity, the throw's speed, the play area. A matchbox and a mountain then fall and land the same way, and a throw cannot send a Splash off the screen. The cost is that the motion is not physically scaled: a real baseball is thrown a hundred times faster than 4 of its radii a second. Where that matters (the curve of a spin, the glide of a disc), the numbers are set to show the real effect in the real order, as the section on materials below says.

Where it lives: the constants at the top of src/physics/hands-on.js.

Pieces and joints: parts that move the way the real thing's do

What it is. A rigid body is one lump. A treasure chest has a lid, and a lid is not a lump: it swings on its hinge and stops when it is open. A recipe's hands block lets a Splash say that. hands.pieces names pieces of the Splash that are bodies of their own, which stay put until you pick one up or knock it. hands.joints says how a part may move.

How Splashery does it. There are five kinds of joint and one more setting (src/physics/joints.js). Hinges, sliders, and dials are driven parts: one number describes each, an angle or a distance. The finger, gravity, a spring, and the stops move that number, and the part is then posed exactly from it, so a part never wobbles off its axis or jitters. Sockets and broken-off pieces are ordinary bodies of the physics world, solid like the rest.

The Treasure Chest with its lid open

A hinge: the chest's lid

A sword standing in a mossy gray stone

A slider: the sword

A whole orange beside a cut orange half showing its wedges

Sockets: the wedges

Two red and white candy canes tied with a green bow

A break: the cane's top

Each part is a solid group of splats that moves whole. The Music Box's dial is on the shelf too.

Why this way. Splashery's rule for effects is that motion must be real motion and not a warped picture: if a scan cannot move a part cleanly, the part is cut out with hard edges, swapped for a kit-built one, or the effect is dropped. A joint that moves one number, and a part that is moved whole by that number, is exactly that rule in code. The same machinery serves a scan: a scan's rig (src/rigs.js) names the parts of a captured Splash, and src/physics/joints.js turns them into pieces.

Exact taps and draw order. The Chess Set is built in tiles, so a tap lands on its own square 99.9% of the time, and two quick taps on the board are a piece and its square, not a double-tap that resets the view. The Puzzle Cube draws its cubies in the current frame's order while a layer turns, so pieces do not pass through each other (Chapter 1 says why the order matters). The Real Orange's lower peel follows the capture's own lower outline, and a capture's rig and its add-on (the kit-built piece that stands in for part of it) appear in the same frame.

Try it: grab a hinge, a slider, and a dial

Drag the lid, the blade, or the knob (or use the sliders under the picture). Each part is posed from one number, shown beside it. Let go of the lid and it falls by its weight and bounces a little off its stop; the blade stays where you leave it, held by friction like the Sword in the Stone; flick the dial and it coasts. Nothing leaves this page.

The joints demo: a chest with its lid half open, a blade partway out of a stone, and a dial turned about a third of the way round, each with its number
Interactive in the web version: splashery.app/manual/moving.html#demo-joints

Ropes, cloth, and soft stretch

What it is. Some things bend: a string, an octopus's arms, a hood, a kite's tail. Splashery's rule against bending scans does not forbid them, because these Splashes are built from kit shapes and their bending is built in.

How Splashery does it. Ropes and cloth are lists of points (nodes) held by distance links, solved with the same position-based method as the rigid bodies, in a solver of its own (src/physics/soft.js). A rope can be slack (it folds and never pushes), has a little stiffness against bending, and can be pinned, can ride a piece (an octopus's arm rides its body), or can follow a finger. A cloth is a grid of nodes, pinned at its edge, that hangs, drapes, and flutters in a wind. A link can snap when it is stretched too far, as a cheese string does. Soft stretch, the third piece, is how the Gummy Bear's splats follow your finger and then wobble back.

Soft stretch is shared by every Food Splash: its Hands-on goes through the same stretch (a recipe's own drag first, its grab second), and a real stretch sounds once when you let go and starts the wobble. The Gummy Bear is the lead example. Jelly Blob (Shapes) is the plainest: a press picks it, it stretches, and it wobbles back and settles in about 1.5 seconds.

Figure to come: Jelly Blob stretched and wobbling back, in three frames.

What moves on screen follows the rule too. A thin, bendy thing (a string, a tentacle) is built from splats that each follow the two nodes they lie between, so a rope bends smoothly and every splat still keeps its shape. A sheet has each splat follow the four corners of its cell. A solid thing on a rope (a bead, a chime, the yo-yo itself) is a rigid piece that rides a node and turns with the link, so it stays solid at every angle.

Try it. Pull the string of the Yo-yo and let go, or hold the Octopus by one arm.

Materials: a thrown thing moves like the real thing

What it is. A basketball, a hockey puck, a paper plane, and a marble do not move alike. A recipe's hands.material names a preset or gives its own numbers, so a thrown Splash bounces, rolls, spins, and flies the way its real counterpart does.

How Splashery does it. There are 27 presets (src/physics/materials.js), from the basketball and the soccer ball to the shuttlecock, the flying disc, and the paper plane. A preset holds the real regulation size and mass of the sport, the bounce its rule book asks for (a basketball dropped from 1.8 m should come back to 1.2 to 1.4 m, which is a bounce of 0.82 to 0.88), and the textbook drag of a sphere (0.47). The drag is the real thing, expressed without units: how many Splash radii a Splash falls before it reaches its top speed, Fr = 2m / (ρ cd π r3) with air at 1.2 kg per cubic meter. Because that number has no units, it holds whatever the Splash's size on screen. A throw spins the Splash by where you held it (a low grab gives backspin, a side grab sidespin), and the spin curves the flight (the Magnus effect). Lift, for a disc, a cap, or a paper plane, and the curve of a spin are set to show in a short flight rather than scaled from real life, in the real direction and the real order: a disc glides more than a cap, and a baseball curves more than a soccer ball.

Try it. Throw the Basketball, the Baseball, and the Paper Plane and watch how each leaves your hand.

Rigid parts and the motion driver

What it is. Everything above, and everything a recipe's drive does, ends in the same place: a few numbers per part for the graphics chip.

How Splashery does it. The MotionDriver in src/motion.js runs once per frame, on the CPU. It eases the controls, runs the Splash's own behaviors clock, calls the recipe's drive, and gathers what comes out into uniforms. For parts, packParts writes three four-number values for each of up to 16 parts (a rotation, a pivot with a scale, and an offset with a visibility), 48 in all. Every splat carries the number of its part; the shader looks up the part and moves the splat: p = pivot + R (p − pivot) × (1 + scale) + offset. This is the same hinge as Chapter 2's, and a part that a joint moves goes through the very same lines. Hands-on does not have a second way to move a splat: for a Splash with pieces, the poses its world computes are merged into the recipe's drive.parts, so a piece moved by a finger and a part turned by a program arrive at the shader identically.

Why this way. The GPU moves every splat, and the CPU only decides a few dozen numbers: a physics world with a few dozen bodies costs almost nothing, and a Splash with 100,000 splats moves as cheaply as one with a hundred. It also keeps the splats solid: a part's splats are all turned by the same rotation, so a part cannot bend. The limit is that a splat can belong to only one part, which is why a Splash has at most 15 parts beside the body (Chapter 2's fold on the number of parts says why).

Where to try it

None of these is behind the labs switch. Chapter 10 says how a recipe asks for hands-on play.