011 - Export System
The Export System enables SVG export, snapshots, video, and exhibition mode.
Why Multiple Export Formats?
Different use cases require different formats:
Vector (SVG): For pen plotters, print, laser cutters. Resolution-independent, precise paths.
Raster (PNG/JPG): For screens, social media. Exactly as displayed.
Video: For animations, social reels, archive.
Exhibition Mode: For galleries. Automated operation, restart on crash, parameter changes.
Components
| Class | File | Function |
|---|---|---|
SVGExporter |
SVGExporter.ts |
Canvas → SVG |
Snapshot |
Snapshot.ts |
Screenshot System |
VideoExporter |
VideoExporter.ts |
Video Recording |
SequencePlayer |
SequencePlayer.ts |
Snapshot Playlists |
ExhibitionRunner |
ExhibitionRunner.ts |
Artwork Playlists |
ExhibitionPackageBuilder |
ExhibitionPackageBuilder.ts |
Build Packages |
SVG Export
Canvas-to-SVG conversion for plotters.
How Does It Work?
The SVGExporter doesn't "translate" canvas pixels – instead, it records all draw calls (lines, circles, arcs) and builds an SVG DOM tree. Each canvas layer becomes an SVG group (<g>).
Why is this complex? Because SVG has no gradient fills like Canvas, and vice versa. Some effects must be approximated.
Layer System
┌─────────────────────────────────────────┐
│ SVG Document │
│ ┌───────────────────────────────────┐ │
│ │ <g id="layer-0"> │◄─── Background Layer
│ │ ┌─────────────────────────────┐ │
│ │ │ <rect fill="..."/> │ │
│ │ └─────────────────────────────┘ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ <g id="layer-1"> │◄─── Shapes Layer
│ │ ┌─────────────────────────────┐ │
│ │ │ <circle cx="..."/> │ │
│ │ │ <path d="..."/> │ │
│ │ └─────────────────────────────┘ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ <g id="layer-2"> │◄─── Effects Layer
│ │ ┌─────────────────────────────┐ │
│ │ │ <polyline points="..."/> │ │
│ │ └─────────────────────────────┘ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘SVGExporter API
| Method | Description |
|---|---|
startDocument(width, height) |
Start SVG Document |
endDocument() |
Complete and Return |
beginLayer(name) |
Create new Layer |
endLayer() |
Complete Layer |
line(x1, y1, x2, y2) |
Draw Line |
circle(cx, cy, r) |
Draw Circle |
rect(x, y, w, h) |
Draw Rectangle |
path(d) |
Draw SVG Path |
polygon(points) |
Draw Polygon |
Gradients and Patterns
The SVGExporter supports:
| Type | SVG Equivalent |
|---|---|
LinearGradient |
<linearGradient> |
RadialGradient |
<radialGradient> |
Pattern |
<pattern> |
Definition:
exporter.defineGradient({
id: 'myGradient',
type: 'linear',
stops: [
{ offset: 0, color: '#FF0000' },
{ offset: 1, color: '#0000FF' },
],
});
exporter.setFill('url(#myGradient)');Pen/Color Management
| Method | Description |
|---|---|
setPen(penId) |
Set Pen (for Plotter) |
setFill(color) |
Fill Color |
setStroke(color, width) |
Stroke Color & Width |
setOpacity(value) |
Transparency |
Plotter Optimization
| Option | Description |
|---|---|
optimizePaths |
Combine connected paths |
sortPaths |
Sort for shortest travel |
groupByColor |
Group by color (= pen) |
Snapshots
Screenshots of the canvas.
Snapshot API
| Method | Description |
|---|---|
take(canvas, opts) |
Create Snapshot |
save(snapshot, path) |
Save to Disk |
toDataURL(canvas) |
As Base64 |
Options
| Option | Type | Description |
|---|---|---|
format |
'png' | 'jpg' |
File Format |
quality |
number |
Quality (0-1) |
scale |
number |
Scale Factor |
includeMetadata |
boolean |
Include Parameters? |
Metadata
Snapshots can contain embedded metadata:
{
"timestamp": "2024-01-15T10:30:00Z",
"artworkId": "sketch-001",
"parameters": { "...": "..." },
"dimensions": { "width": 1920, "height": 1080 }
}Video Export
Record animations as MP4.
Flow
Start Recording Draw Loop Stop Recording
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Configure│──────>│ Record │───────>│ Encode │
│ Encoder │ │ Frames │ │ to MP4 │
└──────────┘ └──────────┘ └──────────┘VideoExporter API
| Method | Description |
|---|---|
configure(opts) |
Configure |
startRecording() |
Start Recording |
addFrame(canvas) |
Add Frame |
stopRecording() |
Stop & Encode |
Options
| Option | Type | Default | Description |
|---|---|---|---|
fps |
number |
30 | Frames per Second |
bitrate |
number |
5000000 | Bitrate (bps) |
codec |
string |
'avc1.42001E' | Video Codec |
format |
'mp4' | 'webm' |
'webm' | Container Format |
Codec Support
| Codec | Quality | Compat | Description |
|---|---|---|---|
vp8 |
Good | Excellent | WebM Standard |
vp9 |
Very Good | Good | WebM New |
avc1 |
Very Good | Excellent | H.264 (MP4) |
SequencePlayer
Plays snapshots as a slideshow.
Concept
A "sequence" is a playlist with snapshots. Each entry has display duration, optional transition, and parameter restore.
SequencePlayer API
| Method | Description |
|---|---|
loadSequence(playlist) |
Load Playlist |
play() |
Start Playback |
pause() |
Pause |
stop() |
Stop |
next() |
Next Image |
previous() |
Previous Image |
Playlist Format
{
"name": "My Sequence",
"items": [
{
"snapshotPath": "snapshot-001.png",
"duration": 5000,
"transition": "fade",
"parameters": { "...": "..." }
}
],
"loop": true
}Transitions
| Transition | Description |
|---|---|
none |
Hard Cut |
fade |
Fade In/Out |
crossfade |
Blend |
slide |
Slide In |
ExhibitionRunner
Automated operation for galleries.
The Problem
In an exhibition, the artwork runs for days without supervision:
- What if it crashes?
- What if someone touches the USB cable?
- How do you change artworks?
The Solution
ExhibitionRunner manages multiple artworks and handles all edge cases:
┌─────────────────────────────────────────────────────────┐
│ ExhibitionRunner │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Scheduler │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │Artwork 1│ │Artwork 2│ │Artwork 3│ │ │
│ │ │ 10 min │ │ 15 min │ │ 10 min │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ HealthMonitor │ │
│ │ • Heartbeat Check │ │
│ │ • Auto-Restart on Crash │ │
│ │ • Log Rotation │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘ExhibitionRunner API
| Method | Description |
|---|---|
loadSchedule(schedule) |
Load Schedule |
start() |
Start Exhibition |
stop() |
Stop Exhibition |
setEmergencyMode(on) |
Emergency Stop |
getStatus() |
Current Status |
Schedule Format
{
"name": "Main Exhibition",
"artworks": [
{
"path": "./artworks/waves/",
"duration": 600,
"parameters": { "speed": 0.5 }
},
{
"path": "./artworks/particles/",
"duration": 900
}
],
"options": {
"loop": true,
"restartOnCrash": true,
"healthCheckInterval": 5000
}
}Health Monitoring
| Check | Interval | Action |
|---|---|---|
| Heartbeat | 5s | Restart if Missing |
| Memory | 60s | Warn at >80% |
| Framerate | 1s | Warn at <15 FPS |
Fallback Behavior
| Event | Reaction |
|---|---|
| Artwork Crashes | Restart + Skip after 3x |
| All Artworks Crash | Show Emergency Slide |
| USB Error | Reload Context |
ExhibitionPackageBuilder
Creates standalone exhibition packages.
Package Structure
exhibition-package/
├── index.html # Entry Point
├── manifest.json # Playlist + Settings
├── artworks/
│ ├── artwork-1/
│ │ ├── bundle.js
│ │ └── assets/
│ └── artwork-2/
├── snapshots/
│ └── emergency.png # Fallback Image
└── launcher/
├── launcher.exe # Windows
├── launcher.app # macOS
└── launcher.sh # LinuxBuilder API
| Method | Description |
|---|---|
addArtwork(path, opts) |
Add Artwork |
setManifest(manifest) |
Set Manifest |
build(outputPath) |
Build Package |
validate() |
Validate Package |
Build Options
| Option | Description |
|---|---|
minify |
Minify Code |
includeLauncher |
Include Launcher |
offlineAssets |
Bundle all Assets |
signCode |
Code Signing (macOS) |