Aesthetic Computer — Apple TV (tvOS) App Plan#
0. Product Intent#
Build a tvOS Apple TV app that functions as a passive, cinematic playback surface for Aesthetic Computer artifacts—primarily pre-rendered MP4 "Tapes" generated by Oven.
Core rule:
Oven renders. Apple TV plays.
Explicit Non-Goals#
- No WebViews / no WebKit
- No KidLisp editor or generative rendering
- No chat, wallets, minting, or comments
- No dense menus or text input
- No feature parity with the website
1. Primary User Experience#
Tape Mode (default)#
- App launch → immediate fullscreen playback
- Sequential playback of tapes
- Auto-advance on end
- Optional loop or shuffle
Siri Remote mapping
- ▶︎ Play / Pause
- ← Previous tape
- → Next tape
- ↑ Show overlay (metadata)
- ↓ Hide overlay
- Long-press ▶︎ (optional): Favorite / Unfavorite
Minimal Overlay (auto-hide)#
Shown only when requested.
Aesthetic Computer
Tape 014 · 3:44 · 1280×720 @ 30fps
Seed: 839201 · Generator: kidlisp-oven
2. Content Model#
Tape (canonical artifact)#
A Tape is a finished audiovisual unit.
Required
id- MP4 URL (H.264 + AAC)
- Duration (seconds)
Recommended
- Title
- Creation timestamp
- Resolution / FPS
- Seed
- Generator name + version
- Tags (tape, gallery, ambient, etc.)
Tape JSON Schema (draft)#
{
"id": "tape-014",
"title": "Tape 014",
"mp4": "https://aesthetic.computer/tapes/014.mp4",
"duration": 224,
"resolution": "1280x720",
"fps": 30,
"seed": 839201,
"generator": "kidlisp-oven",
"created_at": "2025-01-03T10:42:00Z",
"tags": ["tape", "glitch"]
}
Feed Format#
v1
GET /tv/tapes.json
{ "tapes": [Tape] }
v2 (future)
- Multiple feeds: tapes, ambient, highlights, live
3. Oven Responsibilities (Upstream)#
Rendering#
- Deterministic, seeded runs
- Headless Chromium / WebGL / KidLisp
- Fixed resolution
- Fixed framerate
- Fixed duration
Encoding (Apple-safe, frozen)#
- Video: H.264
- Pixel format: yuv420p
- Audio: AAC LC
- Constant framerate
- No alpha, no exotic color spaces
Outputs#
- MP4 files
- Optional HLS (live / long-form)
- JSON feed index
Hosting#
- HTTPS only
- CDN-friendly
- Permissive CORS for JSON + MP4
- Long cache headers for MP4s
4. Apple TV App Responsibilities#
Must Do#
- Fetch JSON feeds
- Parse into models
- Cache feed locally
- Fullscreen playback via AVPlayer
- Auto-advance and retry logic
- Minimal overlays
Must Not Do#
- No WebViews
- No private APIs
- No rendering or heavy computation
- No complex UI hierarchy
5. App Modes (Phased)#
Phase 1#
- Tape Mode only
Phase 2#
- Gallery Mode (slower pacing)
- Local favorites
Phase 3#
- Ambient Mode (screensaver-grade loops)
Phase 4#
- Live Mode (HLS + fallback to archive)
6. Architecture Overview#
Tech Stack#
- SwiftUI (tvOS)
- AVKit / AVPlayer
- URLSession + Codable
- On-disk caching (Application Support)
Module Sketch#
AppleTVApp/
├─ App/
│ ├─ AestheticTVApp.swift
│ └─ AppState.swift
├─ Models/
│ ├─ Tape.swift
│ └─ Feed.swift
├─ Services/
│ ├─ FeedService.swift
│ ├─ CacheService.swift
│ └─ ReachabilityService.swift
├─ Player/
│ ├─ PlayerCoordinator.swift
│ ├─ PlayerView.swift
│ └─ OverlayView.swift
└─ Utilities/
└─ Logger.swift
7. Playback Logic#
- Maintain
tapes: [Tape] - Track
currentIndex - On playback end:
- Advance sequentially or shuffle
- Wrap if loop enabled
- On error:
- Skip tape
- Continue playback
- Overlay never blocks playback
8. Physical Apple TV Build & Test Plan#
Goal#
Validate real-device playback ASAP.
Fastest MVP Test#
- Create tvOS SwiftUI app
- Hardcode one Oven MP4 URL
- Fullscreen AVPlayer playback
- Pause / resume via remote
- Run for 5–10 minutes
Pairing#
Apple TV:
- Settings → Remotes and Devices → Remote App and Devices
Xcode:
- Window → Devices and Simulators → Pair Apple TV
Signing#
- Use Automatic Signing
- Resolve provisioning before feed logic
Definition of Done#
- App installs from Xcode
- Smooth playback
- Relaunch resumes correctly
9. Performance Constraints#
- Prefer 720p for reliability, 1080p optional
- H.264 baseline / main profile
- Stable framerate
- App runs unattended for hours
10. Security & Compliance#
- Public HTTPS endpoints only
- No WebKit
- No private APIs
- App Store–safe playback behavior
11. Phased Delivery#
Phase 1 — MVP Player#
- Single hardcoded MP4
- Play / pause
- Real-device validation
Phase 2 — Feed Playback#
- Fetch + cache JSON feed
- Auto-advance playlist
- Prev / next controls
Phase 3 — Experience Polish#
- Gallery / Ambient modes
- Better pacing + overlays
Phase 4 — Live#
- HLS stream support
- Archive fallback
12. Success Criteria#
- Launch → playback in under 1 second
- Zero dropped frames
- Stable for long sessions
- Feels calm, intentional, gallery-grade
Summary#
This app is a television for Aesthetic Computer, not a computer on a television.
Oven produces finished tapes.
Apple TV presents them beautifully.
Integration Notes#
Existing Infrastructure#
MongoDB tapes Collection#
The codebase already has a tapes collection with this schema:
{
_id: ObjectId,
code: string, // 3-char short code (e.g., "xyz")
slug: string, // piece-timestamp format
user: string | null, // auth0 subject or null for guests
when: Date,
bucket: string, // "user-aesthetic-computer" or "art-aesthetic-computer"
at: { // ATProto record (if synced)
rkey: string,
uri: string,
cid: string
},
nuked: boolean
}
Existing API Endpoints#
GET /api/tv (tv.mjs)
- Already fetches tapes with ATProto video URLs
- Supports
?types=tapefilter - Returns structured feed with video URLs from ATProto PDS
- Response includes:
id,type,code,slug,owner,when,acUrl,media.videoUrl
GET /api/get-tape (get-tape.mjs)
- Query by
?code=xyzor?slug=... - Returns:
slug,code,when,bucket,user,nuked
Video Hosting#
- Primary: ATProto PDS blobs at
https://at.aesthetic.computer/xrpc/com.atproto.sync.getBlob - Source ZIPs: DigitalOcean Spaces (
art-aesthetic-computerbucket) - Oven Server:
oven.aesthetic.computerprocesses ZIPs → MP4s
Recommended New Endpoint: /api/tv/tapes.json#
Create a dedicated Apple TV feed endpoint:
// system/netlify/functions/tv-tapes.mjs
// GET /api/tv/tapes - Returns tape feed for Apple TV
export async function handler(event) {
const database = await connect();
const tapes = await fetchTapes(database.db, { limit: 100 });
// Transform to Apple TV schema
const feed = tapes.map(t => ({
id: t.code,
title: `Tape ${t.code.toUpperCase()}`,
mp4: t.media.videoUrl,
duration: null, // Would need to store in MongoDB
resolution: "1280x720",
fps: 30,
seed: null,
generator: "kidlisp-oven",
created_at: t.when,
tags: ["tape"]
}));
return respond(200, {
tapes: feed,
total: feed.length,
lastUpdated: new Date().toISOString()
});
}
Integration Recommendations#
-
Extend
tapesMongoDB schema to include:duration(seconds) - from metadata.jsonresolution- from metadata.jsonfps- from metadata.jsonseed- if available from piece
-
Update Oven to store this metadata when processing
-
Create
/api/tv/tapes.jsonendpoint that:- Returns Apple TV-friendly JSON
- Filters to only tapes with valid ATProto video blobs
- Includes CORS headers for tvOS fetch
- Caches response (5-10 minute TTL)
-
Video URL Pattern:
https://at.aesthetic.computer/xrpc/com.atproto.sync.getBlob?did={did}&cid={videoCid}These are direct H.264 MP4 URLs suitable for AVPlayer.
Data Flow#
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Recording │────▶│ Oven │────▶│ ATProto │
│ (ZIP) │ │ (H.264) │ │ (Blob) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ │
┌─────────────┐ │
│ MongoDB │◀───────────┘
│ (tapes) │
└─────────────┘
│
▼
┌─────────────┐
│/api/tv/tapes│
│ .json │
└─────────────┘
│
▼
┌─────────────┐
│ Apple TV │
│ App │
└─────────────┘
Phase 1 MVP Implementation Path#
- Week 1: Create tvOS app scaffold with hardcoded test MP4 URL
- Week 2: Implement
/api/tv/tapes.jsonendpoint using existingfetchTapes()logic - Week 3: Connect app to endpoint, implement playlist navigation
- Week 4: Polish overlay UI, test on physical device