Desktop MP4 export renders frames from a hidden BrowserWindow via capturePage, pipes PNGs to a bundled ffmpeg (libx264/yuv420p/+faststart), and writes the file through an atomic temp-file swap. It shares scene validation (shared/renderValidation.cjs) and the render.html renderer with the standalone Puppeteer server, which is hardened in parallel. Render worker & main (electron/): - abortable render loop with an inactivity watchdog and cancel plumbing - atomic temp-file + rename so a cancel/failure never truncates a pre-existing target (#7); stdin EPIPE / early-exit / backpressure-hang handled (#8) - HiDPI frames normalized to canvas size (#10); odd dimensions padded to even (#13, also server); backgroundThrottling disabled (#12) - reject canvases larger than the display (#11); single-render guard; isolated render session that blocks network requests (#5) Tests: - Electron harnesses (scripts/verify-*.cjs) for capture, export, cancel, timeout, assets, background, capture-fidelity, resolution, odd-dims, aggregated by verify-desktop-render.cjs - server smoke + e2e and shared validation unit tests - headless hook (src/devtools/headless.ts) to run the in-app suite under browser/electron hosts; pixel-diff util + capture fixtures (test/) - dev_docs plans: integration, fixes (#7-#13), and the e2e/capture strategy Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A timeline-based animation editor that runs in your browser.
RUWIO is a browser-native authoring tool for short interactive animations. It pairs a Konva-driven canvas with a keyframe timeline, exports a self-contained HTML/JS bundle, and ships everything as a Vue 3 single-page app you can serve from any static host.
Highlights
- Keyframes everywhere — animate position, rotation, scale, fill, stroke, shadow, and shape-specific properties. Optional separated position / rotation tracks. End keyframes terminate a layer cleanly.
- Effects
- Kaleidoscope — radial repeats of a layer or a clip instance, with per-arm time offset for time-pinwheel effects.
- Wiggle — deterministic procedural shake (position + rotation).
- Masks — destination-in / destination-out cascade, scoped to track groups, clips, and SVG assets.
- Asset kinds — rect, circle, polygon (with vertex / bezier editing), freeform path, text (14 bundled Google fonts + custom .woff2 / .ttf upload), images, audio, button-with-states, nested SVG assets, and reusable clips with their own internal timeline.
- Scenes — multiple pages per project; jumpToScene / jumpTo / jumpToFrame scripted actions; markers; per-scene background video and audio tracks.
- Render parity — the canvas2d offline renderer, the Konva live stage, and the exported runtime all share the same sampling math. What you see on stage is what gets exported.
- Eyedropper colour picking, motion-path overlay, global seconds / frames display toggle, custom font upload to localStorage — the polish kit.
Quickstart
npm install
npm run dev # start the editor at http://localhost:5200
npm run type-check # vue-tsc strict pass
npm run build # production bundle to dist/
npm run pack # zip dist/ into dist-archive/
Electron desktop build
RUWIO can also run as an Electron desktop app. The desktop shell uses the
same Vite-built app from dist/ plus the compiled Electron entry points in
dist-electron/.
npm install
npm run desktop:dev # start Vite, then launch Electron against localhost
npm run desktop:pack # build an unpacked desktop app into release/
npm run desktop:build # build platform installer/package artifacts into release/
desktop:pack is the quickest compile check because it produces an unpacked
app without creating an installer. desktop:build uses electron-builder and
targets AppImage on Linux, NSIS on Windows, and DMG on macOS.
Desktop MP4 export runs inside Electron and uses ffmpeg from
resources/bin/<platform>/ in packaged builds. The build scripts run
npm run stage:ffmpeg -- --optional, which copies FFMPEG_PATH or ffmpeg on
PATH into bin/<platform>/ when available. For release artifacts, replace
that local copy with redistributable static ffmpeg binaries for Linux, Windows,
and macOS.
If no bundled binary is present, MP4 export still falls back to FFMPEG_PATH
or system ffmpeg and reports a clear error when neither can be executed.
Before shipping bundled ffmpeg, document the binary source, version, configured
codecs, and GPL/LGPL obligations for that build.
MP4 render server
MP4 export is handled by the optional render server in server/. It binds to
127.0.0.1:6000 by default and requires a token before it will accept render
jobs.
cd server
npm ci
HOST=127.0.0.1 PORT=6000 RENDER_TOKEN="choose-a-long-random-token" npm start
Point the app at the server with matching Vite env values:
VITE_RENDER_SERVER=http://localhost:6000 VITE_RENDER_TOKEN="choose-a-long-random-token" npm run dev
The server exposes HOST=0.0.0.0 only when explicitly requested. If you expose
it beyond localhost, set RENDER_ALLOWED_ORIGINS to the exact app origins that
should be allowed. Chromium sandboxing stays enabled by default; use
CHROME_NO_SANDBOX=1 only for a deployment environment that cannot run the
sandbox.
The included docker-compose.yml keeps the published host port on
127.0.0.1:6000 and requires RENDER_TOKEN in the environment before it will
start.
Render-server checks:
npm run test:render-server # health/auth/validation route smoke
npm run test:render-server:e2e # renders a tiny MP4 and verifies it with ffprobe
The E2E check requires Chromium, ffmpeg, and ffprobe. Set
PUPPETEER_EXECUTABLE_PATH when Chromium is not at /usr/bin/chromium.
Stack
- Vue 3 (
<script setup>) + Pinia state - Konva for the live canvas (vue-konva bindings)
- Vite for dev / build
- TypeScript everywhere; tests live in
src/devtools/tests.tsand run inside the app via the in-browser test overlay gifenc,upng-jsfor image-sequence exports
Project layout
src/
components/ Vue SFCs (CanvasStage, Timeline, PropertiesPanel, …)
composables/ Reusable hooks (theme, time-format, eyedropper, …)
lib/ Pure logic: render.ts, exportPlayer.ts, interpolate.ts,
wiggle.ts, kaleido.ts, ruwFile.ts (zip pack/unpack), …
stores/ Pinia stores
types/ Scene + project type definitions
i18n/ English + German translations
devtools/ In-app test runner + tests
public/
fonts/ Bundled .woff2 files
Tests
The test runner lives inside the app. Open the editor (npm run dev)
and hit Ctrl+Shift+Alt+T to toggle the overlay; from there you can
run individual suites or the whole pile. The runner, all suites and
the components they exercise are plain TypeScript / Vue, so the tests
run the real code path — no separate harness.

