Purpose
Implement a horizontally-scrolling card carousel where cards translate continuously at constant velocity, with partial cards visible at the canvas edges to imply infinite content. The conveyor IS the transition — there's no scene cut.
Params (params.schema.json)
{
"id": "uturn-conveyor",
"cards": [], // array of card content objects
"card_width_px": 720,
"card_height_px": 1280,
"card_gap_px": 80,
"card_radius_px": 32,
"card_tilt_deg": 0, // 0 for axis-aligned, ±5–8° for SP-E
"velocity_px_per_sec": 360, "direction": "right-to-left", "easing": "linear", // ALWAYS linear; constant velocity is the point
"start_offset_px": 0, // where the first card starts "loop": false, // infinite loop (for testing) or finite traverse
"cards_visible_at_once": 2.3 // partial cards at edges }
Each card in `cards[]`:
```jsonc
{
"bg_color": "#FF5A21",
"tilt_deg": -5, // overrides global card_tilt_deg
"content": { /* archetype-specific card content */ }
}
Behaviour
- Render a horizontal flex strip with all cards laid out.
- Compute
stripX = -velocity_px_per_sec * frame / fps + start_offset_px. - Apply
transform: translateX(stripX)to the strip. - Each card has its own tilt baked in via
transform: rotate(card_tilt_deg). - Apply a CSS
overflow: hiddenmask on the parent so partial cards clip cleanly at the canvas edges.
Implementation: a Remotion component using useCurrentFrame() and direct transform: translateX (no spring; linear motion).
Style pack overrides
SP-E (chaos-card)
card_tilt_deg: random per card, range -8° to +8°, seeded.velocity_px_per_sec: 360 (default).- Cards have sticker-chip-pop overlays that pop in 8 frames after each card enters the visible window.
- Music beat anchor: not required (conveyor is continuous).
SP-A — not used (Marshmallow uses card-stagger-slide + step-pan).
SP-D — not used (Magazine uses scrolling-index-dual-rail, which is vertical).
When to use
- Phone-card-carousel archetype (A9).
- "Feed-scrolling" UI mocks where the user is showing a continuously-scrolling app feed.
- Any spot where the carousel rhythm IS the transition (no scene cuts within the carousel).
When NOT to use
- Discrete pricing carousels (use
card-stagger-slide+step-paninstead — see A4). - When the user wants to dwell on each card (conveyor is constant velocity; use step-pan for dwells).
- In SP-A/B/C/D/F/G — pack-incompatible.
Quality Checks
- Velocity is constant (no easing on translateX).
- Partial cards at edges clip cleanly (no white gap, no overflow).
- Card tilts are visible but moderate (±5–8° max).
- Cards don't accelerate at scene start or decelerate at scene end.
- Strip total width allows the conveyor to run for the full scene duration without exposing the strip's leftmost or rightmost edge.
Failure Modes
- Conveyor accelerates at start. Easing is not linear — verify
easing: linearand no spring config. - Strip end exposes white canvas.
cards_visible_at_once×card_width + card_gap× scene_duration > strip total width. Add more cards (or duplicate cards if loop=true). - Cards visibly snap into place.
velocity_px_per_secis too low — bump to 300+ for noticeable continuous motion. - Cards visibly blur from too-fast motion. Velocity too high (> 600 px/s on 1080w canvas). Lower to 300-450.