Flow
Flow helpers let you compose multiple items in time. They can contain shapes, arrays of shapes, or functions that return shapes.
Helpers
sequence
sequence(a, b, c) plays items back-to-back.
Duration is computed from each item by inspecting its modifiers. If a shape has no animated modifiers, its duration is 0.
sequence
Items play one after another.
DSL
Scene(() => [
sequence(
Circle(36)
.fill('#f97316')
.scale({ from: 0, to: 1, duration: 600, ease: 'easeOut' }),
Rect(120, 70)
.fill('#0ea5e9')
.rotate({ from: 0, to: Math.PI / 8, duration: 600, ease: 'easeOut' }),
Triangle(120, 90, { direction: 'down' })
.fill('#22c55e')
.opacity({ from: 0, to: 1, duration: 500 })
),
])
parallel
parallel(a, b, c) plays items together.
parallel
Items run at the same time.
DSL
Scene(() => [
parallel(
Circle(50).fill('#0f172a').opacity({ from: 0.4, to: 1, duration: 900, loop: true }),
Rect(120, 70).fill('#38bdf8').rotate({ from: -0.2, to: 0.2, duration: 900, loop: true }),
Star(5, 70, 30).fill('#facc15').scale({ from: 0.8, to: 1.1, duration: 900, loop: true })
),
])
on
on(start, ...items) schedules items at a specific TimeRef.
on
Schedule a burst after scene start.
DSL
Scene(() => [
Circle(40)
.fill('#0f172a')
.opacity({ from: 0.2, to: 0.8, duration: 800, loop: true }),
on('scene+600',
Star(5, 60, 26)
.fill('#22d3ee')
.scale({ from: 0, to: 1.2, duration: 500 })
.opacity({ from: 1, to: 0, duration: 600 })
),
])
when
when(predicate, ...items) runs items conditionally. The predicate receives current time in ms.
when
Show a label only after time > 1200ms.
DSL
Scene(() => [
Circle(36).fill('#f97316').move({ x: [-120, 120], duration: 1200, loop: true }),
when((t) => t > 1200,
Text('GO', { fontSize: 44, fontWeight: 700 })
.fill('#ffffff')
.opacity({ from: 0, to: 1, duration: 400 })
.at({ x: 0, y: 70 })
),
])
Complex Examples
Flow Composition
Sequence plus scheduled secondary motion.
DSL
Scene(() => [
sequence(
Circle(42)
.fill('#f97316')
.scale({ from: 0, to: 1, duration: 600, ease: 'easeOut' }),
Rect(140, 80)
.fill('#0ea5e9')
.rotate({ from: 0, to: Math.PI / 8, duration: 600 })
),
on('scene+1200',
Circle(10)
.fill('#22c55e')
.move({ y: [-80, 80], duration: 900, loop: true })
),
])
Nested Flow
Parallel groups inside a timed sequence.
DSL
Scene(() => [
sequence(
() => [
Circle(28)
.fill('#38bdf8')
.scale({ from: 0.4, to: 1, duration: 600, loop: true })
.at({ x: -40, y: 0 }),
Circle(10)
.fill('#facc15')
.move({ y: [-40, 40], duration: 600, loop: true })
.at({ x: 40, y: 0 }),
],
() => [
Rect(120, 70)
.fill('#0f172a')
.opacity({ from: 0.4, to: 1, duration: 600, loop: true }),
Text('Flow', { fontSize: 28, fontWeight: 600 })
.fill('#ffffff'),
]
)
])
Conditional Burst
Conditional burst + scheduled echo.
DSL
Scene(() => [
Circle(34)
.fill('#0f172a')
.scale({ from: 0.9, to: 1.1, duration: 700, loop: true, ease: 'easeInOut' }),
when((t) => t > 1000,
Star(5, 60, 28)
.fill('#22d3ee')
.opacity({ from: 1, to: 0, duration: 600, loop: true })
),
on('scene+1400',
Circle(8)
.fill('#a3e635')
.move({ x: [-60, 60], duration: 900, loop: true, ease: 'easeInOut' })
)
])