Public
Like
flipdot
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Status: Current Last Updated: 2025-01-09 Protocol Version: 3.0
v2.0 (Old): Server returned single highest-priority content. Driver managed priority queue and interruptions.
v3.0 (New): Server returns complete ordered playlist. Driver plays sequentially (simple FIFO).
| Feature | v2.0 | v3.0 |
|---|---|---|
| Response field | content: Content | playlist: Content[] |
| Status values | "updated" | "no_change" | "clear" | "updated" | "clear" |
| Priority handling | Driver-side queue | Server-side ordering |
| Interruptions | Driver pauses/resumes | None (server controls order) |
| PlaybackMode fields | loop, loop_count, priority, interruptible | loop, loop_count only |
Purpose: Driver polls for complete playlist
Method: GET
Headers:
User-Agent: FlipDot-Driver/3.0
Content-Type: application/json
Authorization: Bearer {token}
Status Codes:
200 OK- Playlist returned401 Unauthorized- Auth failed500 Internal Server Error- Server error
Body:
interface ContentResponse {
status: "updated" | "clear";
playlist: Content[]; // Ordered list, first plays immediately
poll_interval_ms: number; // >= 1000
}
Example (Multiple items):
{ "status": "updated", "playlist": [ { "content_id": "urgent-alert", "frames": [{ "data_b64": "...", "width": 28, "height": 14, "duration_ms": 5000 }], "playback": { "loop": false } }, { "content_id": "clock-12:00", "frames": [{ "data_b64": "...", "width": 28, "height": 14, "duration_ms": null }], "playback": { "loop": false } } ], "poll_interval_ms": 30000 }
Example (Clear):
{ "status": "clear", "playlist": [], "poll_interval_ms": 30000 }
interface ContentResponse {
status: "updated" | "clear";
playlist: Content[];
poll_interval_ms: number;
}
Rules:
- If
statusis"updated",playlistSHOULD be non-empty (can be empty if server has no content) - If
statusis"clear",playlistMUST be empty poll_interval_msMUST be >= 1000
interface Content {
content_id: string;
frames: Frame[];
playback?: PlaybackMode;
metadata?: object;
}
Rules:
framesMUST contain 1-1000 frames- All frames MUST have identical dimensions
- Total size MUST NOT exceed 5MB
interface Frame {
data_b64: string;
width: number;
height: number;
duration_ms?: number | null;
metadata?: object;
}
Rules:
data_b64MUST be valid base64 (little-endian packed bits)widthandheightMUST match display (validated by driver)duration_msofnullor0means display indefinitely
interface PlaybackMode {
loop?: boolean;
loop_count?: number | null;
}
Rules:
loop_countcan only be set ifloopistrue(server manages ordering)priorityandinterruptibleremoved
- Playlist Ordering: Server decides content order based on priorities, urgency, schedules, etc.
- Stateless Operation: Each request computes fresh playlist (or uses cache)
- Content Management: Add/remove content from sources
- Rendering: Convert text/graphics to packed bit frames
- Polling: Poll at
poll_interval_msintervals - FIFO Playback: Play playlist items in order
- Frame Timing: Advance frames based on
duration_ms - Serial Communication: Send frames to hardware
On status: "updated":
- Driver replaces entire queue with new playlist
- If
playlist[0].content_idmatches current content ID, preserve frame timing - Otherwise, start fresh with
playlist[0]
On status: "clear":
- Driver clears all content
- Display shows blank
// Server maintains content sources with priorities
const sources = [
{ id: "clock", priority: 10, generate: () => generateClock() },
{ id: "alert", priority: 90, generate: () => generateAlert() },
];
// Generate playlist
async function generatePlaylist(): Promise<Content[]> {
// Get all active content
const content = await Promise.all(
sources.map(s => s.generate())
);
// Sort by priority (highest first)
content.sort((a, b) => b.priority - a.priority);
// Return ordered list
return content;
}
- Change
content: Contenttoplaylist: Content[] - Remove
status: "no_change"logic - Return all active content, not just highest priority
- Remove
priorityandinterruptiblefrom Content objects - Keep priority in server-side source management for ordering
- Accept
playlist: Content[]instead ofcontent: Content - Remove priority queue logic
- Remove interruption/resume logic
- Simplify to FIFO queue
- Preserve frame timing when
playlist[0]has same ID
Same as v2.0: Little-endian packed bits, row-major order, base64 encoded.
See v2.0 spec Section 4 for details.
Same as v2.0: Bearer token or API key authentication required.
Valid minimal response:
{ "status": "updated", "playlist": [ { "content_id": "test", "frames": [{ "data_b64": "AAAA", "width": 28, "height": 14 }] } ], "poll_interval_ms": 30000 }
Test server compliance:
- Returns
playlistarray (notcontentobject) - Status is either
"updated"or"clear"(not"no_change") - PlaybackMode does not include
priorityorinterruptible - Multiple content items ordered correctly
- All frames have matching dimensions
Document Status: Ready for Implementation