performance-audit
Profile and optimize ProAnimate's canvas frame rates, export times, memory usage, and store subscription overhead. Use when adding new layers, stores, or heavy computations.
SKILL.mdAutoAnimation/.claude/skills/performance-audit/SKILL.md
GitHubPerformance Audit
When to Use
- Before adding a new canvas layer type (particles, crowd, audio-reactive)
- After modifying the animation engine (interpolate, spring, CompositionPlayer)
- When users report slow preview playback or long export times
- When adding new Zustand stores that canvas layers subscribe to
- Before any release
Frame Budget
Target: 60fps preview, 30fps minimum acceptable
| Budget | Time per frame | What fits |
|---|---|---|
| 60fps | 16.6ms | Canvas render + all layer compositing |
| 30fps | 33.3ms | Degraded but usable |
| <30fps | >33.3ms | Unacceptable — find the bottleneck |
Profiling Steps
1. Canvas Frame Rate
// Add to CompositionPlayer.tsx tick loop temporarily
const start = performance.now()
// ... render frame ...
const elapsed = performance.now() - start
if (elapsed > 16.6) console.warn(`Frame budget exceeded: ${elapsed.toFixed(1)}ms`)
2. Store Subscription Audit
134 Zustand stores exist. Check which ones cause canvas re-renders:
grep -r "useFrame\|usePlaybackStore\|useCanvasStore" src/remotion/ src/components/canvas/ --include="*.tsx" -l
Every store subscription in a canvas component re-renders at playback fps. Minimize subscriptions using selectors:
// BAD: subscribes to entire store, re-renders on any change
const store = useCanvasStore()
// GOOD: subscribes only to what's needed
const zoom = useCanvasStore(s => s.zoom)
3. Memory Profiling
Watch for leaks in:
- Three.js: geometries, materials, textures not disposed on scene change
- PixiJS: GPU textures not destroyed
- Audio buffers: ElevenLabs TTS blobs held after dialogue changes
- Lottie: animation instances not stopped/destroyed
- Stores: 134 stores loaded eagerly — check which ones hold large data
// Check memory in DevTools Console
performance.measureUserAgentSpecificMemory().then(r => console.log(r))
4. Export Performance
The export pipeline renders frame-by-frame to offscreen canvas via WebCodecs + mp4-muxer.
- Profile:
console.time('frame-N')around each frame render in videoExport.ts - Bottlenecks: Three.js scene render, PixiJS canvas capture, SVG filter application
- Target: export should not exceed 3x realtime (30s video exports in <90s)
Red Flags
- Any
useEffectwithout cleanup in canvas/remotion components - Any
setIntervalorrequestAnimationFramewithout cancellation - Stores with
subscribe()calls that never unsubscribe - Large arrays (viseme timelines, keyframe data) copied on every frame
- CSS
filterorbackdrop-filteron elements that re-render at fps