Documentation Index
Fetch the complete documentation index at: https://mintlify.com/BintzGavin/helios/llms.txt
Use this file to discover all available pages before exploring further.
This example demonstrates how to create animations using the HTML5 Canvas API with Helios. Perfect for custom graphics, data visualizations, and high-performance animations.
Overview
The canvas animation example shows how to:
- Set up a full-screen canvas element
- Respond to Helios state changes
- Draw frame-synchronized graphics
- Handle canvas resizing
Setup
Create the HTML canvas
Start with an HTML file containing a full-screen canvas element:<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas Composition</title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
background-color: #111;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="composition-canvas"></canvas>
<script type="module" src="./src/main.ts"></script>
</body>
</html>
Initialize canvas and Helios
Set up the canvas context and initialize Helios:import { Helios, HeliosState } from '@helios-project/core';
const canvas = document.getElementById('composition-canvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
console.log(`Canvas resized to ${canvas.width}x${canvas.height}`);
};
// Resize immediately and on window resize
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
const duration = 5; // seconds
const fps = 30;
// Initialize Helios
const helios = new Helios({
duration,
fps
});
// Bind to document.timeline so the Renderer can drive us
helios.bindToDocumentTimeline();
Create the draw function
Implement your drawing logic based on the current frame:function draw(currentFrame: number) {
const time = currentFrame / fps * 1000; // in ms
const progress = (time % (duration * 1000)) / (duration * 1000);
const { width, height } = canvas;
// Clear canvas
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, width, height);
const x = progress * width;
const y = height / 2;
const radius = 50;
// Draw moving circle
ctx.fillStyle = 'royalblue';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
// Draw rotating square
const squareSize = 100;
ctx.save();
ctx.translate(width / 2, height / 2);
ctx.rotate(progress * Math.PI * 2);
ctx.fillStyle = 'tomato';
ctx.fillRect(-squareSize / 2, -squareSize / 2, squareSize, squareSize);
ctx.restore();
}
Subscribe to state changes
Connect your draw function to Helios state updates:// Subscribe to Helios state changes
helios.subscribe((state: HeliosState) => {
draw(state.currentFrame);
});
// Make helios available globally for debugging
declare global {
interface Window {
helios: Helios;
}
}
window.helios = helios;
How it works
State subscription
Helios uses a subscription model to notify your code when the timeline state changes. Each time Helios updates (during playback or seeking), your draw function is called with the current frame number.
helios.subscribe((state: HeliosState) => {
// state.currentFrame - the current frame number
// state.isPlaying - whether playback is active
// state.progress - normalized progress (0 to 1)
draw(state.currentFrame);
});
Frame-based rendering
Canvas animations in Helios are frame-based, not time-based. This ensures:
- Deterministic rendering - same frame always produces same output
- Perfect seeking - any frame can be rendered independently
- Export reliability - rendered videos are frame-perfect
Canvas resizing
Proper canvas sizing is critical for quality output. The example handles window resize events and updates the canvas dimensions accordingly:
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};
window.addEventListener('resize', resizeCanvas);
Key concepts
- Subscribe pattern: Listen to state changes instead of using requestAnimationFrame
- Frame numbers: Use
currentFrame to calculate animation progress
- Deterministic drawing: Same frame should always produce identical output
- Canvas context: Store and reuse the 2D rendering context
- Clear efficiently: Only clear the portions of canvas that change
- Batch operations: Group similar drawing operations together
- Save/restore context: Use
ctx.save() and ctx.restore() for transforms
- Avoid allocations: Reuse objects instead of creating new ones each frame
Complete example
View the complete source code in the simple-canvas-animation directory.
Next steps