Cinder Pike Card

  • Skia
  • Reanimated
  • Motion sensors

A collectible card that rises out of a stone well, then answers to how you hold the phone. Its entrance and its idle motion are the same transform, handing over from one to the other rather than fighting for the same node.

One clock, and windows on it

There are no chained animations. Each beat is written as a window relative to the moment the reveal fired — the card fades over 40 to 300 ms, the well glow runs from −80 to 220 ms — and since smoothsteps across whichever window it is given. A sentinel far below zero marks 'not started', so an unfired scene evaluates to a resting pose rather than a special case.

export const IDLE = -1e9;export function armed(at: number) {  'worklet';  return at > IDLE / 2;}// …const progress = Math.max(0, Math.min(1, (now - start) / (end - start)));return progress * progress * (3 - 2 * progress);

The entrance hands over to your hands

One transform list carries both states, blended by the lift's own progress. While rest is high the card holds its entry offset, its smaller scale and a −14° swing; as the lift completes those fade out and the device-tilt terms fade in on the same rotation. There is never a frame where two systems both claim the card.

const rest = 1 - t;return [  { perspective: plan.perspective },  { translateX: plan.fromX * rest },  { translateY: plan.fromY * rest },  { scale: plan.fromScale + (1 - plan.fromScale) * t },  { rotateY: SWING * rest + motion.renderedX.value * TILT * tiltScale * t },  { rotateX: -motion.renderedY.value * TILT * tiltScale * t },];

A window drawn in the art's own coordinates

The card's opening is a hand-drawn path in the illustration's native 1064×1478 space, with conic corners where the frame curves. Keeping it in source coordinates means the art and its mask scale together at any size, and one region is deliberately allowed outside the opening so the spear can break the frame.

export const CINDER_SOURCE = { width: 1064, height: 1478 } as const;export const CINDER_APERTURE_SVG = [  'M 34 32 L 252 32 L 252 84',  'C 335 60 403 39 503 42',  // …].join(' ');export const CINDER_SPEAR_BREAKOUT = { x: 980, y: 710, width: 130, height: 180 } as const;

Tuning

  • 100
  • 100
<CinderPikeCard params={{ tilt: 100, parallax: 100 }} fireSignal={0} />