Sequencing functions help you organize and coordinate multiple animations across your composition’s timeline. These utilities make it easy to create complex choreography without manual frame calculations.
sequence()
Calculates the local time and progress of a sequence relative to a start frame.
import { sequence } from '@helios-project/core' ;
const seq = sequence ({
frame: currentFrame ,
from: 30 ,
durationInFrames: 60
});
if ( seq . isActive ) {
const opacity = seq . progress ;
}
Parameters
Sequence configuration object Current frame number from your composition
Starting frame for this sequence
Duration in frames. If undefined, sequence is active indefinitely after start.
Returns
SequenceResult object with the following properties:
Frame number relative to the sequence start (frame - from)
Alias for localFrame, for clarity
Normalized progress from 0 to 1. Always 0 if no duration specified.
True if the current frame is within the sequence’s time range
Examples
Basic sequence
import { sequence } from '@helios-project/core' ;
function SequencedElement ({ frame }) {
const seq = sequence ({
frame ,
from: 30 ,
durationInFrames: 60
});
if ( ! seq . isActive ) return null ;
return (
< div style = {{ opacity : seq . progress }} >
Visible from frame 30 - 90
</ div >
);
}
Using local frame
const seq = sequence ({ frame , from: 30 , durationInFrames: 90 });
if ( seq . isActive ) {
// seq.localFrame goes from 0 to 89
const x = interpolate ( seq . localFrame , [ 0 , 90 ], [ 0 , 800 ]);
}
Infinite sequence
// Sequence starts at frame 60 and runs until end of composition
const seq = sequence ({ frame , from: 60 });
if ( seq . isActive ) {
// Active from frame 60 onward
}
series()
Arranges items sequentially, automatically calculating start times based on each item’s duration.
import { series } from '@helios-project/core' ;
const scenes = series ([
{ id: 'intro' , durationInFrames: 60 },
{ id: 'main' , durationInFrames: 120 },
{ id: 'outro' , durationInFrames: 60 }
]);
// scenes[0].from = 0
// scenes[1].from = 60
// scenes[2].from = 180
Parameters
Array of items with durationInFrames and optional offset properties
Starting frame for the first item in the series
Returns
Array<T & { from: number }> - Original items with from property added
Examples
Sequential scenes
import { series , sequence } from '@helios-project/core' ;
const scenes = series ([
{ name: 'Intro' , durationInFrames: 90 },
{ name: 'Main' , durationInFrames: 180 },
{ name: 'Credits' , durationInFrames: 60 }
]);
function VideoComposition ({ frame }) {
return (
<>
{ scenes . map (( scene ) => {
const seq = sequence ({
frame ,
from: scene . from ,
durationInFrames: scene . durationInFrames
});
if (!seq.isActive) return null;
return <Scene key = {scene. name } name = {scene. name } progress = {seq. progress } /> ;
})}
</>
);
}
With offsets
const scenes = series ([
{ id: 'a' , durationInFrames: 60 },
{ id: 'b' , durationInFrames: 60 , offset: - 10 }, // Starts 10 frames before 'a' ends
{ id: 'c' , durationInFrames: 60 , offset: 20 } // Starts 20 frames after 'b' ends
]);
// scenes[0].from = 0
// scenes[1].from = 50 (60 - 10)
// scenes[2].from = 130 (50 + 60 + 20)
Custom start frame
const delayedScenes = series (
[
{ durationInFrames: 60 },
{ durationInFrames: 90 }
],
30 // Start at frame 30
);
// delayedScenes[0].from = 30
// delayedScenes[1].from = 90
stagger()
Staggers items at a fixed interval, useful for animating lists and grids.
import { stagger } from '@helios-project/core' ;
const items = stagger (
[ 'Apple' , 'Banana' , 'Cherry' ],
10 // 10 frames between each
);
// items[0].from = 0
// items[1].from = 10
// items[2].from = 20
Parameters
Array of items to stagger
Number of frames between each item’s start time
Frame to start the first item at
Returns
Array<T & { from: number }> - Items with from property added
Examples
Staggered list animation
import { stagger , interpolate } from '@helios-project/core' ;
function StaggeredList ({ items , frame }) {
const staggeredItems = stagger ( items , 8 ); // 8 frames delay between each
return (
< div >
{ staggeredItems . map (( item , i ) => {
const opacity = interpolate (
frame ,
[ item . from , item . from + 20 ],
[ 0 , 1 ],
{ extrapolateRight: 'clamp' }
);
const y = interpolate (
frame ,
[ item . from , item . from + 20 ],
[ 20 , 0 ],
{ extrapolateRight: 'clamp' }
);
return (
< div key = { i } style = {{ opacity , transform : `translateY( ${ y } px)` }} >
{ item }
</ div >
);
})}
</ div >
);
}
Grid stagger
const grid = [
[ 'A' , 'B' , 'C' ],
[ 'D' , 'E' , 'F' ],
[ 'G' , 'H' , 'I' ]
];
const flatGrid = grid . flat ();
const staggeredGrid = stagger ( flatGrid , 5 , 30 ); // Start at frame 30
shift()
Shifts the start times of all items by a fixed offset.
import { series , shift } from '@helios-project/core' ;
const scenes = series ([
{ durationInFrames: 60 },
{ durationInFrames: 90 }
]);
const delayedScenes = shift ( scenes , 30 ); // Delay everything by 30 frames
Parameters
Array of items with a from property
Number of frames to shift by (can be negative)
Returns
T[] - Items with updated from values
Examples
Delaying a sequence
const scenes = series ([
{ durationInFrames: 60 },
{ durationInFrames: 90 }
]);
const delayed = shift ( scenes , 60 ); // Start 60 frames later
Creating variations
const baseSequence = stagger ([ 'A' , 'B' , 'C' ], 10 );
const earlyVersion = shift ( baseSequence , - 20 );
const lateVersion = shift ( baseSequence , 40 );
Common patterns
Chapter-based composition
import { series , sequence } from '@helios-project/core' ;
const chapters = series ([
{ title: 'Opening' , durationInFrames: 90 },
{ title: 'Presentation' , durationInFrames: 240 },
{ title: 'Demonstration' , durationInFrames: 180 },
{ title: 'Conclusion' , durationInFrames: 90 }
]);
function VideoComposition ({ frame }) {
return (
<>
{ chapters . map (( chapter ) => {
const seq = sequence ({
frame ,
from: chapter . from ,
durationInFrames: chapter . durationInFrames
});
return seq.isActive ? (
<Chapter key = {chapter. title } { ... chapter } progress = {seq. progress } />
) : null ;
})}
</>
);
}
Overlapping transitions
const scenes = series ([
{ id: 'a' , durationInFrames: 90 },
{ id: 'b' , durationInFrames: 90 , offset: - 15 }, // 15 frame overlap
{ id: 'c' , durationInFrames: 90 , offset: - 15 }
]);
function Scene ({ scene , frame }) {
const seq = sequence ({
frame ,
from: scene . from ,
durationInFrames: scene . durationInFrames
});
// Fade in during first 15 frames, fade out during last 15 frames
let opacity = 1 ;
if ( seq . localFrame < 15 ) {
opacity = seq . localFrame / 15 ;
} else if ( seq . localFrame > scene . durationInFrames - 15 ) {
opacity = ( scene . durationInFrames - seq . localFrame ) / 15 ;
}
return < div style ={{ opacity }}> Scene content </ div > ;
}
Multi-track timeline
const videoTrack = series ([
{ type: 'clip' , source: 'intro.mp4' , durationInFrames: 120 },
{ type: 'clip' , source: 'main.mp4' , durationInFrames: 300 }
]);
const overlayTrack = stagger (
[ 'Title' , 'Subtitle' , 'Call to action' ],
60 ,
30 // Start at frame 30
);
function Timeline ({ frame }) {
return (
<>
< VideoLayer clips = { videoTrack } frame = { frame } />
< OverlayLayer items = { overlayTrack } frame = { frame } />
</>
);
}
Combining with animation helpers
import { series , sequence , interpolate , spring , Easing } from '@helios-project/core' ;
const elements = stagger (
[ 'First' , 'Second' , 'Third' , 'Fourth' ],
12
);
function AnimatedElements ({ frame }) {
return elements . map (( item , i ) => {
const startFrame = item . from ;
// Use spring for natural entrance
const scale = spring ({
frame: frame - startFrame ,
fps: 30 ,
from: 0 ,
to: 1 ,
config: { stiffness: 150 , damping: 12 }
});
// Use interpolate with easing for position
const x = interpolate (
frame ,
[ startFrame , startFrame + 24 ],
[ - 100 , 0 ],
{ easing: Easing . cubic . out , extrapolateRight: 'clamp' }
);
const opacity = frame >= startFrame ? 1 : 0 ;
return (
< div
key = { i }
style = {{
opacity ,
transform : `translateX( ${ x } px) scale( ${ scale } )`
}}
>
{ item }
</ div >
);
});
}