A mascot rigged in code, not animated
A to-do list mascot who watches your finger, giggles when poked, flies when flung, leans when you tilt the phone and cheers your last task.
React Native Skia · Reanimated · Worklets · Gesture Handler · Expo Sensors · Expo Haptics
1Demo
2Flow
- 1Touch the screenYouhis eyes follow your finger; lift it and they wander
- 2Poke himYoua light haptic tap, a surprised face, then a giggle
- 3Fling himYouhe flies, bounces off the walls and lands with a squash; a hard landing makes him dizzy
- 4Tilt the phonePhonegravity turns with it, and he leans to stay upright
- 5Tick the last taskYouhe jumps, cheers and throws confetti, with a success haptic
3How it fits
4Code
if (r.y >= floor) {
const impact = r.vy;
r.y = floor;
if (impact > 380) {
r.vy = -impact * 0.36;
r.vx *= 0.7;
r.sy = Math.max(0.68, 1 - impact / 4200);
r.syV = 0;
r.landed = Math.max(r.landed, impact);
if (impact > 1500 && r.expr !== 'cheer') set(r, 'dizzy', 2.6);
} else {
r.vy = 0;
if (!r.grounded && r.expr === 'wee') set(r, 'giggle', 0.8);
r.grounded = true;
}
} else if (r.y < floor - 0.5) {
r.grounded = false;
}
}
// Expression timers.
if (r.expr !== 'idle') {
r.exprT -= dt;
if (r.exprT <= 0) set(r, 'idle', 0);
}
const e = r.expr;
// Squash and stretch: a spring back to 1, stretched along a fall.
const airStretch = r.grounded || r.dragging ? 0 : Math.min(0.1, Math.abs(r.vy) / 9000);
const waddle = r.walking ? Math.abs(Math.sin(r.t * 13)) * 0.04 : 0;
const giggleBounce = e === 'giggle' ? Math.sin(r.t * 26) * 0.035 : 0;
[r.sy, r.syV] = spring(r.sy, r.syV, 1 + airStretch + giggleBounce + waddle, 420, 13, dt);
// Lean: against the tilt to stay upright in the world, into his own motion, wobbling when dizzy.
const wobble = r.dizzy * Math.sin(r.t * 7) * 0.14;
const leanTarget = -tilt * 0.8 + Math.max(-0.4, Math.min(0.4, r.vx / 3000)) + wobble;
[r.lean, r.leanV] = spring(r.lean, r.leanV, leanTarget, 140, 11, dt);A landing squashes him by how hard he hit, a hard one makes him dizzy, and springs ease squash and lean back to their targets.
5Decisions
A rig drawn in code every frame over a Rive or Lottie animation
a timeline plays whether you're there or not; here every motion has a cause
Expressions as short timed states over switching between animations
a state only sets the targets the springs chase, so poke, giggle and dizzy blend
A small Gfx drawing interface over calling Skia directly
the same drawing code renders offline in CanvasKit, which is how the design was tuned
A scripted finger on the real pointer path over a staged animation for the video
the take runs through the same code a real finger hits, so his reactions are his own
