PAPER-2026-003

Animation Spec Architecture

One Source, Two Renderers: Shared Specifications for Svelte and Remotion

Methodology 8 min read Intermediate

Abstract

When building educational content for CREATE SOMETHING, we needed animations for both web interactions (Svelte) and video exports (Remotion). Rather than duplicate animation logic across two frameworks, we developed a spec-driven architecture where shared specifications define what happens—timing, phases, keyframes—while each renderer interprets how to execute. This paper documents the architecture, its philosophical foundations in the Subtractive Triad, and practical implementation patterns.

2
Renderers
5
Canon Reveals
1
Source of Truth
3
Packages Using

1. The Problem

CREATE SOMETHING teaches through multiple modalities: interactive web experiences and exportable videos. Both need the same animations—a tool receding into use, an IDE dissolving into a terminal, text revealing character by character.

The naive approach: duplicate the animation logic in both frameworks.

// The duplication problem

// Svelte version (LessonRemotion.svelte)
const hammerOpacity = $derived(
  $progress < 0.2 ? 1 :
  $progress < 0.6 ? 1 - (($progress - 0.2) / 0.4) * 0.7 :
  Math.max(0, 0.3 - (($progress - 0.6) / 0.4) * 0.3)
);

// Remotion version (ToolReceding.tsx)
const hammerOpacity = interpolate(progress, [0, 0.2, 0.6, 1], [1, 1, 0.3, 0]);

This violates DRY (Don't Repeat Yourself)—one leg of the Subtractive Triad. When we update timing in one place, we must remember to update it in another. The animations drift. Consistency erodes.

The Subtractive Triad: Heidegger (remove obscurity), Rams (remove excess), DRY (remove disconnection). The duplication problem violates the third principle.

2. The Architecture

The solution: Animation Specs—TypeScript definitions that describe what should happen, without prescribing how.

// Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                    ANIMATION SPECS                          │
│  motion-studio/src/specs/                                   │
│                                                             │
│  • types.ts         Schema for all animations               │
│  • tool-receding.ts Heidegger's ready-to-hand               │
│  • ide-vs-terminal.ts Chrome → Canvas                       │
│  • canon-reveals.ts Text animation styles                   │
└─────────────────────────────────────────────────────────────┘
                         │
         ┌───────────────┴───────────────┐
         ▼                               ▼
┌─────────────────────┐       ┌─────────────────────┐
│   SVELTE RENDERERS  │       │  REMOTION RENDERERS │
│                     │       │                     │
│ LessonRemotion.svelte│      │ ToolReceding.tsx    │
│ CanonReveal.svelte   │      │ IDEvsTerminal.tsx   │
│                      │      │ KineticText.tsx     │
│ For: Web             │      │ For: Video export   │
└─────────────────────┘       └─────────────────────┘

Spec Structure

Each animation spec defines:

Metadata

  • id — Unique identifier
  • name — Human-readable name
  • description — What it demonstrates
  • duration — Total time in ms

Phases

  • id — Phase identifier
  • label — Display text
  • start — Progress (0-1)
  • end — Progress (0-1)

Keyframes

  • at — Progress value
  • opacity — 0 to 1
  • scale — Transform scale
  • blur — Filter blur

Reveal

  • text — Final text
  • style — fade, mask, typewriter
  • startPhase — When to begin

3. Canon Reveal Styles

The five Canon-aligned text reveal styles embody aspects of the Subtractive Triad:

StylePhilosophyDuration
DECODESignal emerges from noise (DRY)3000ms
UNCONCEALMENTTruth emerges from concealment (Heidegger)3000ms
TYPEWRITERMeditative, deliberate (Terminal-first)3000ms
THRESHOLDBinary presence (Rams)1500ms
MASKThe text was always there (Subtractive)1500ms
LIVE DEMO — DECODE

Cipher resolves — The signal emerges from noise

4. Implementation Pattern

Step 1: Define the Spec

// motion-studio/src/specs/tool-receding.ts

export const toolRrecedingSpec: AnimationSpec = {
  id: 'tool-receding',
  name: 'Tool Receding',
  description: "The hammer disappears when hammering.",
  duration: 5000,
  
  phases: [
    { id: 'vorhandenheit', label: 'Present-at-hand', start: 0, end: 0.2 },
    { id: 'transition', label: 'Focus shifts', start: 0.2, end: 0.7 },
    { id: 'zuhandenheit', label: 'Ready-to-hand', start: 0.7, end: 1 },
  ],
  
  reveal: {
    text: 'The hammer disappears when hammering.',
    startPhase: 0.7,
  },
};

Step 2: Import in Svelte

// LessonRemotion.svelte

import { animationSpecs, getCurrentPhase } from '$lib/animations/specs';

const spec = animationSpecs[compositionId];
const duration = spec?.duration ?? 5000;

// Use spec values
const currentPhase = $derived(spec ? getCurrentPhase(spec, $progress) : null);
const revealText = spec?.reveal?.text;

Step 3: Import in Remotion

// ToolReceding.tsx

import { toolRrecedingSpec } from '../../specs/tool-receding';

const spec = toolRrecedingSpec;
const totalFrames = (spec.duration / 1000) * (spec.fps ?? 30);

// Use spec phases
const currentPhase = spec.phases.find(p => 
  progress >= p.start && progress < p.end
);

Key Insight: The spec defines timing as progress (0-1), not frames or milliseconds. Each renderer converts to its native unit: Svelte uses tweened with duration, Remotion uses interpolate with frame counts.

5. Benefits

DimensionWithout SpecsWith Specs
ConsistencyManual syncAutomatic
Timing Updates2+ files1 file
Phase LabelsHardcodedShared
DocumentationSeparateIn spec
New Animations2 implementationsSpec + renderers

6. Packages Using This Pattern

motion-studio

  • • Canonical spec definitions
  • • Remotion compositions
  • • Video export pipeline

lms

  • • LessonRemotion component
  • • Interactive lessons
  • • Spec-driven animations

io

  • • Canon reveal demos
  • • Paper visualizations
  • • Teaching modalities

7. Why Not Build One Renderer?

A natural question: why not build a unified renderer that works for both web and video?

Web (Svelte)

  • • Needs interactivity (play, pause, scrub)
  • • Uses reactive primitives ($state, $derived)
  • • Renders to DOM continuously
  • • Performance: 60fps in browser

Video (Remotion)

  • • Frame-perfect rendering
  • • Uses React + interpolate()
  • • Renders to frames sequentially
  • • Output: MP4, WebM, GIF

Different constraints, different tools. Svelte excels at reactive web UIs. Remotion excels at programmatic video. Forcing one tool to do both would violate Rams: "Good design is as little design as possible."

The Canon Principle: Use the right tool for the job. Share the specification, not the implementation.

8. Conclusion

The Animation Spec Architecture demonstrates how the Subtractive Triad applies to cross-framework development:

  • DRY: Single source of truth for animation timing and phases
  • Heidegger: The spec reveals the essence; renderers handle mechanics
  • Rams: Right tool for each job—Svelte for web, Remotion for video

The result: animations that look identical across web and video, maintained from a single specification, with each renderer optimized for its medium.

The Spec-Driven Animation Principle

Specify
what happens
Render
how it looks
Maintain
one source

"The spec describes what happens. The renderer decides how. Both serve the whole."