The fastest way to animate expressive 2D characters and motion using a timeline + vector stage, without fighting the tool.
Inamate is an open-source, collaborative, timeline-centric 2D animation platform built for the web. Our goal is to combine the simplicity of classic animation tools with modern, powerful features, focusing on speed and an animation-first user experience.
Built with a Go backend, a Go-based WebAssembly (WASM) core engine, and a TypeScript/React frontend.
- Real-time Collaborative Editing - Work together with your team in real-time
- Timeline-Centric Workflow - Keyframe animation with property tracks and easing
- Vector Graphics Engine - Create and animate shapes directly on canvas
- WebAssembly Performance - Native-speed rendering powered by Go/WASM
- Instant Playback - Preview your animations at full framerate
- Fast: Get from a blank canvas to a moving character in minutes, not hours.
- Clear: Simple scenes feel simple. Complex scenes stay navigable and performant.
- Approachable Power: Features like rigging are designed as optional tools for posing, not mandatory engineering exercises.
- Animation First: The user experience prioritizes timing, posing, and iteration over complex technical setup.
Inamate uses a command buffer architecture for rendering, where the Go WASM engine owns the retained scene graph and makes all rendering decisions:
Go WASM Engine Frontend (Browser)
┌─────────────────────┐ ┌─────────────────────┐
│ Document + Timeline │ │ │
│ ↓ │ │ │
│ Build Scene Graph │ │ │
│ (transforms, │ DrawCommand[] │ │
│ opacity, │ ──────────────→ │ Execute on Canvas2D │
│ visibility, │ (JSON buffer) │ ctx.fill(path) │
│ path generation) │ │ ctx.stroke(path) │
│ ↓ │ │ ctx.drawImage(img) │
│ Compile DrawCommands │ │ ctx.transform(...) │
└─────────────────────┘ └─────────────────────┘
The engine compiles the scene into a DrawCommand[] buffer containing path operations, transforms, fills, and strokes. The frontend receives this buffer and executes it on a Canvas2D context. This is analogous to a GPU command queue: the engine is the "driver" deciding what to draw; the browser's Canvas2D (which is GPU-accelerated) handles rasterization.
Why not full pixel rendering in WASM (Figma-style)?
Figma uses a custom C++ renderer compiled to WASM with years of optimization. For a Go WASM engine, the command buffer approach is superior because:
- Canvas2D is GPU-accelerated — the browser already uses hardware rendering for path fills, strokes, and image blits. Software rasterization in Go would be CPU-only and significantly slower.
- No Go WASM rasterizer — Go's ecosystem lacks a battle-tested pure-Go vector rasterizer that compiles to WASM (popular libraries like
fogleman/ggdepend on C/cairo). - Lower overhead — emitting a command buffer is O(nodes); software rasterization would be O(pixels) for every frame.
- Browser optimizations — we benefit from antialiasing, sub-pixel rendering, and compositing that browsers have spent years optimizing.
The frontend operates across two coordinate spaces: world space (post-transform, what you see on canvas) and parent-local space (where document mutations happen). Converting between them requires affine matrix inversion through the parent hierarchy. This pipeline powers alignment, distribution, snapping, and any spatial operation that needs to reason about screen positions while mutating local transforms.
See Transform Pipeline for the full design, including renderer swapability.
frontend/ React/TypeScript UI, Canvas2D command executor
src/engine/ WASM bridge, Stage render loop, draw command interpreter
src/components/ React components (canvas, timeline, properties)
src/types/ Document model, operation types
backend-go/ Go backend
cmd/wasm/ WASM entry point (JS bindings)
cmd/server/ HTTP/WebSocket server
internal/engine/ Scene graph, draw command compilation, animation evaluation
internal/collab/ Real-time collaboration, operation handling
internal/document/ Document model
All document mutations flow through an operation-based system that supports undo/redo and real-time collaboration:
User Action → commandDispatcher.dispatch(operation)
→ Optimistic local apply (immediate UI update)
→ Send to server via WebSocket
→ Server validates and broadcasts to other clients
→ Undo: commandDispatcher.undo() inverts and applies
This project is developed in phases, with each phase building on the last.
Goal: A solo animator can create and export a complete short animation.
- Core vector drawing engine (bezier paths, shapes, strokes)
- Timeline with layers, keyframes, and frame scrubbing
- Transform animations (position, scale, rotation, skew, anchor)
- Real-time collaboration via WebSocket (presence, cursors, locking)
- Onion skinning (previous frames blue, next frames orange, configurable depth)
- Easing curves (12 presets: linear, easeIn/Out, cubic, back, elastic, bounce)
- Export to PNG, PNG sequence, MP4, GIF, WebM, and HTML
- Undo/redo (per-user local stacks)
- Editable cubic bezier easing curves (custom curve editor UI)
- Mask layers
Goal: Make Inamate a compelling choice for character animators.
- Symbols/Components with nested timelines
- Text tool (animatable font size, family, weight, alignment)
- SVG import (path, rect, ellipse, circle, line, polygon, polyline)
- Group selection and animation (click selects group, double-click enters)
- Grid overlay and snap-to-grid (dot grid, configurable sizes)
- Copy/paste, duplicate, arrow key nudge
- Alignment and distribution tools
- Multi-scene support with scene tabs
- Image paste and drag-drop (raster placement)
- Approachable Rigging: Bones (FK/IK), auto-weighting, and visual controls
- Switch/Pose Layers: For managing mouths, hands, and expressions
- Graph Editor: Fine-grained control over animation curves and timing
- Audio placement on timeline
- Comments and annotations
Goal: Unlock advanced workflows without overwhelming new users.
- Deformation System: Envelope, curve, and mesh deformers
- Driver System: Allow any property to drive any other property for smart rigs
- Non-destructive Effects Stack: Layer-based blurs, glows, and color adjustments
Goal: Solidify Inamate as a professional-grade, extensible tool.
- Optional Node Graph: For complex rigging and compositing
- Asset & Library System: Reusable, versioned assets
- Modern Publishing: Engine-friendly exports and open interchange formats
# Clone and setup
git clone https://github.com/your-username/inamate.git
cd inamate
task setup
# Start development servers
task dev- Frontend: http://0.0.0.0:5173
- Backend API: http://localhost:8080
$ VITE_API_URL="https://inamate-api.your-domain.com" npm run build
$ ALLOWED_ORIGINS="https://inamate.your-domain.com,https://0.0.0.0:5173" ./backend-go/bin/server| Command | Description |
|---|---|
task dev |
Start frontend and backend dev servers |
task build |
Build all artifacts (WASM, backend, frontend) |
task test |
Run all tests |
task migrate:up |
Apply database migrations |
task infra:stop |
Stop Docker services |
| Layer | Technology |
|---|---|
| Frontend | TypeScript, React, Vite |
| Backend | Go |
| Core Engine | Go compiled to WebAssembly |
| Database | PostgreSQL |
| Real-time | WebSocket |
| Infrastructure | Docker, Task |
We welcome contributions! Please check the issues tab to find areas where you can help. For new features or significant changes, please open a discussion first to outline your idea.
