Skip to main content
This example demonstrates how to build a Helios composition using React. It showcases React-specific patterns including custom hooks for frame synchronization and JSX-based rendering.

Setup

Install Helios core and React dependencies:
npm install @helios-project/core react react-dom

Implementation

Custom hook for frame synchronization

Create a custom hook to subscribe to Helios frame updates:
// hooks/useVideoFrame.ts
import { useState, useEffect } from 'react';
import { Helios } from '@helios-project/core';

export function useVideoFrame(helios: Helios) {
    const [frame, setFrame] = useState(helios.getState().currentFrame);

    useEffect(() => {
        // Update local state when helios state changes
        const update = (state: { currentFrame: number }) => setFrame(state.currentFrame);

        // Subscribe returns an unsubscribe function
        return helios.subscribe(update);
    }, [helios]);

    return frame;
}
The hook uses React’s useState to track the current frame and useEffect to manage the subscription lifecycle. The cleanup function automatically unsubscribes when the component unmounts.

Main component

Build your composition using JSX and the useVideoFrame hook:
// App.tsx
import { Helios } from '@helios-project/core';
import { useVideoFrame } from './hooks/useVideoFrame';

const duration = 5;
const fps = 30;
const helios = new Helios({ duration, fps });

// Bind to document timeline so it can be driven by the player/renderer
helios.bindToDocumentTimeline();

// Expose to window for debugging/player control
if (typeof window !== 'undefined') {
    (window as any).helios = helios;
}

export default function App() {
    const frame = useVideoFrame(helios);

    // Calculate animation values
    const progress = frame / (duration * fps);

    // Example: Opacity 0->1 over first second (30 frames)
    const opacity = Math.min(1, frame / 30);

    // Example: Scale pulse
    const scale = 1 + Math.sin(progress * Math.PI * 4) * 0.2; // 2 pulses

    // Example: Rotation
    const rotation = progress * 360;

    return (
        <div style={{
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'center',
            height: '100%',
            color: 'white',
            fontFamily: 'sans-serif'
        }}>
            <div style={{
                width: 200,
                height: 200,
                backgroundColor: '#61dafb',
                borderRadius: 20,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                opacity: opacity,
                transform: `scale(${scale}) rotate(${rotation}deg)`,
                boxShadow: '0 0 20px rgba(97, 218, 251, 0.5)'
            }}>
                <span style={{
                    fontSize: '2rem',
                    fontWeight: 'bold',
                    color: '#282c34',
                    transform: `rotate(-${rotation}deg)` // Keep text upright
                }}>
                    React
                </span>
            </div>

            <div style={{ marginTop: 40, textAlign: 'center' }}>
                <p>Frame: {frame.toFixed(2)}</p>
                <p>Progress: {(progress * 100).toFixed(1)}%</p>
                <p>FPS: {fps}</p>
            </div>
        </div>
    );
}

Key React patterns

Hook-based state management

The useVideoFrame hook encapsulates Helios subscription logic and provides a clean React-style API. It automatically handles cleanup through the useEffect return function.

Inline styles for dynamic animations

Calculate animation values in the component body and apply them via inline styles. This approach works well for frame-by-frame animations where values change on every render.

Frame-based calculations

Convert frames to progress values, opacity, scale, and rotation using simple math. Each render cycle receives a new frame number, triggering recalculation of all animation values.

Usage

Run the development server:
npm run dev
The composition will render with live frame updates. The Helios instance is exposed on window.helios for debugging and player control.

Build docs developers (and LLMs) love