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.
Helios integrates seamlessly with Vue’s reactive system through composables that leverage Vue’s ref and computed APIs.
Quick start
Install dependencies
npm install @helios-project/core vue
Create the useVideoFrame composable
composables/useVideoFrame.ts
import { ref, onUnmounted, type Ref } from 'vue';
import type { Helios } from '@helios-project/core';
export function useVideoFrame(helios: Helios): Ref<number> {
const frame = ref(helios.getState().currentFrame);
const update = (state: { currentFrame: number }) => {
frame.value = state.currentFrame;
};
const unsubscribe = helios.subscribe(update);
onUnmounted(() => {
unsubscribe();
});
return frame;
}
Create your first animation
<script setup lang="ts">
import { Helios } from '@helios-project/core';
import { useVideoFrame } from './composables/useVideoFrame';
// Initialize Helios singleton
const duration = 5;
const fps = 30;
const helios = new Helios({ duration, fps });
helios.bindToDocumentTimeline();
// Expose to window for debugging/player control
if (typeof window !== 'undefined') {
(window as any).helios = helios;
}
const frame = useVideoFrame(helios);
</script>
<template>
<div class="container">
<div
class="box"
:style="{
opacity: Math.min(1, frame / 30),
transform: `scale(${Math.min(1.5, 0.5 + frame / 150)}) rotate(${frame * 2}deg)`
}"
>
Vue DOM
</div>
<div class="info">Frame: {{ frame.toFixed(2) }}</div>
</div>
</template>
<style scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: white;
font-family: sans-serif;
}
.box {
width: 200px;
height: 200px;
background-color: #42b883;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: bold;
border-radius: 20px;
box-shadow: 0 0 20px rgba(66, 184, 131, 0.5);
border: 4px solid #35495e;
}
.info {
margin-top: 2rem;
font-size: 1.5rem;
}
</style>
Animation approaches
DOM animations
Canvas animations
Animate Vue components using reactive bindings:<script setup>
import { computed } from 'vue';
import { useVideoFrame } from './composables/useVideoFrame';
const frame = useVideoFrame(helios);
const progress = computed(() => frame.value / (duration * fps));
const styles = computed(() => ({
opacity: Math.min(1, frame.value / 30),
transform: `translateX(${progress.value * 500}px) rotate(${frame.value * 2}deg)`
}));
</script>
<template>
<div :style="styles">
Animated content
</div>
</template>
Use refs and watchers to draw frame-by-frame animations:<script setup>
import { ref, watch, onMounted } from 'vue';
import { Helios } from '@helios-project/core';
import { useVideoFrame } from './composables/useVideoFrame';
const duration = 5;
const fps = 30;
const helios = new Helios({ duration, fps });
helios.bindToDocumentTimeline();
if (typeof window !== 'undefined') {
window.helios = helios;
}
const canvasRef = ref(null);
const frame = useVideoFrame(helios);
const draw = () => {
const canvas = canvasRef.value;
if (!canvas) return;
const ctx = canvas.getContext('2d');
const { width, height } = canvas;
// Clear
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, width, height);
const time = frame.value / fps * 1000;
const progress = (time % (duration * 1000)) / (duration * 1000);
// Draw Vue-y visuals
const cx = width / 2;
const cy = height / 2;
const size = 200;
ctx.save();
ctx.translate(cx, cy);
// Pulse effect
const scale = 1 + Math.sin(progress * Math.PI * 2) * 0.1;
ctx.scale(scale, scale);
// Rotate
ctx.rotate(progress * Math.PI * 2);
// Vue Triangle (Outer)
ctx.beginPath();
ctx.moveTo(0, size / 2);
ctx.lineTo(size / 2, -size / 2);
ctx.lineTo(-size / 2, -size / 2);
ctx.closePath();
ctx.fillStyle = '#42b883'; // Vue Green
ctx.fill();
// Vue Triangle (Inner)
ctx.beginPath();
ctx.moveTo(0, size / 4);
ctx.lineTo(size / 4, -size / 2);
ctx.lineTo(-size / 4, -size / 2);
ctx.closePath();
ctx.fillStyle = '#35495e'; // Vue Dark Blue
ctx.fill();
ctx.restore();
// Text
ctx.fillStyle = 'white';
ctx.font = '24px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(`Frame: ${frame.value}`, cx, cy + size / 1.5 + 50);
};
// React to frame changes
watch(frame, draw);
// Handle resize
onMounted(() => {
const canvas = canvasRef.value;
if (!canvas) return;
const resize = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
draw();
};
window.addEventListener('resize', resize);
resize();
});
</script>
<template>
<canvas ref="canvasRef" style="width: 100%; height: 100%; display: block;"></canvas>
</template>
Animation helpers
Helios provides utility functions for common animation patterns:
<script setup>
import { computed } from 'vue';
import { interpolate, spring } from '@helios-project/core';
import { useVideoFrame } from './composables/useVideoFrame';
const frame = useVideoFrame(helios);
// Interpolate x position: 0 -> 200 over frames 0-60
const x = computed(() =>
interpolate(frame.value, [0, 60], [0, 200], { extrapolateRight: 'clamp' })
);
// Spring scale: 0 -> 1 starting at frame 0
const scale = computed(() =>
spring({
frame: frame.value,
fps: 30,
from: 0,
to: 1,
config: { stiffness: 100 }
})
);
</script>
<template>
<div :style="{
transform: `translateX(${x}px) scale(${scale})`,
width: '100px',
height: '100px',
background: 'hotpink'
}">
Animated
</div>
</template>
Sequencing components
Sequence component
Create time-based sequences that show/hide content:
<script setup>
import { computed, inject, provide } from 'vue';
import { sequence } from '@helios-project/core';
const props = defineProps({
from: { type: Number, default: 0 },
durationInFrames: { type: Number, required: true }
});
const parentFrame = inject('videoFrame', 0);
const sequenceState = computed(() =>
sequence({
frame: parentFrame.value,
from: props.from,
durationInFrames: props.durationInFrames
})
);
const isActive = computed(() => sequenceState.value.isActive);
const relativeFrame = computed(() => sequenceState.value.relativeFrame);
provide('videoFrame', relativeFrame);
</script>
<template>
<div v-if="isActive">
<slot />
</div>
</template>
Series component
Automatically sequence child components one after another:
<script setup>
import { useSlots, h, computed } from 'vue';
const slots = useSlots();
const children = computed(() => {
if (!slots.default) return [];
let currentFrom = 0;
return slots.default().map((vnode) => {
const duration = vnode.props?.durationInFrames || 0;
const newVnode = h(vnode, { from: currentFrom });
currentFrom += duration;
return newVnode;
});
});
</script>
<template>
<component :is="child" v-for="(child, i) in children" :key="i" />
</template>
Usage example
<script setup>
import { ref, provide, onUnmounted } from 'vue';
import { Helios } from '@helios-project/core';
import Sequence from './components/Sequence.vue';
import Series from './components/Series.vue';
const helios = new Helios({ duration: 5, fps: 30 });
helios.bindToDocumentTimeline();
if (typeof window !== 'undefined') {
window.helios = helios;
}
const frame = ref(0);
const unsubscribe = helios.subscribe((state) => {
frame.value = state.currentFrame;
});
onUnmounted(() => {
unsubscribe();
});
provide('videoFrame', frame);
</script>
<template>
<div class="container">
<h1>Vue Animation Helpers</h1>
<div>Root Frame: {{ frame.toFixed(2) }}</div>
<Series>
<!-- Sequence 1: 0-30 frames -->
<Sequence :durationInFrames="30">
<div class="box red">Seq 1</div>
</Sequence>
<!-- Sequence 2: 30-60 frames -->
<Sequence :durationInFrames="30">
<div class="box blue">Seq 2</div>
</Sequence>
</Series>
</div>
</template>
Best practices
Initialize Helios in setup
Create the Helios instance in the <script setup> block to ensure it’s only created once:<script setup>
import { Helios } from '@helios-project/core';
// Good: Single instance per component mount
const helios = new Helios({ duration: 5, fps: 30 });
helios.bindToDocumentTimeline();
</script>
Use computed for derived values
Leverage Vue’s computed properties for animation calculations:<script setup>
import { computed } from 'vue';
const frame = useVideoFrame(helios);
const progress = computed(() => frame.value / (duration * fps));
const rotation = computed(() => progress.value * 360);
</script>
Always unsubscribe in onUnmounted to prevent memory leaks:<script setup>
import { onUnmounted } from 'vue';
const unsubscribe = helios.subscribe(update);
onUnmounted(() => {
unsubscribe();
});
</script>
Use provide/inject for frame context
Share frame state across component trees using Vue’s dependency injection:<!-- Parent -->
<script setup>
import { provide } from 'vue';
provide('videoFrame', frame);
</script>
<!-- Child -->
<script setup>
import { inject } from 'vue';
const frame = inject('videoFrame');
</script>
TypeScript support
Helios provides full TypeScript support for Vue 3:
import type { Helios } from '@helios-project/core';
import type { Ref } from 'vue';
export function useVideoFrame(helios: Helios): Ref<number> {
const frame = ref<number>(helios.getState().currentFrame);
// ...
return frame;
}
Next steps
Animation helpers
Learn about interpolation, spring physics, and easing functions
Canvas rendering
Create high-performance canvas animations
Sequences
Build complex multi-scene animations
Export videos
Render your animations to video files