A modern, evolved clone of the Atari Video Music (C-240, 1977) — a dedicated audio visualizer that generates real-time graphics driven by music. Built as a native tvOS application for Apple TV 4K.
The app plays music from the user's Apple Music library and renders generative visuals on the TV in response to the audio signal. Six distinct visual modes can be cycled via the Siri Remote.
Three systems. That's it.
- Uses
ApplicationMusicPlayerfor independent playback (doesn't affect the system Music app) - Requests Apple Music authorization on launch
- Browse albums, playlists, and search the Apple Music catalog
- Now Playing overlay with track title, artist, album art — fades out after 4 seconds
- Standard transport controls via Siri Remote
Real-time frequency and amplitude extraction using AVAudioEngine with vDSP (Accelerate framework).
Extracted data every frame:
| Parameter | Range | Description |
|---|---|---|
| Bass | 0.0–1.0 | 20–250 Hz (kick drums, sub-bass) |
| Low-Mid | 0.0–1.0 | 250–500 Hz (warmth, body) |
| Mid | 0.0–1.0 | 500–2000 Hz (vocals, snare) |
| High-Mid | 0.0–1.0 | 2000–6000 Hz (presence, articulation) |
| High | 0.0–1.0 | 6000–20000 Hz (air, cymbals) |
| Amplitude | 0.0–1.0 | Overall RMS level |
| Transient | 0.0–1.0 | Sharp attack impulse (decays quickly) |
| Spectral Centroid | 0.0–1.0 | Weighted avg frequency (dark↔bright) |
All values smoothed with exponential moving average to prevent visual jitter.
MusicKit's ApplicationMusicPlayer does not expose PCM audio buffers. Apple provides no documented API to tap into MusicKit's audio output stream.
Approach used: Microphone Loopback
AVAudioEnginetaps the device's input node (microphone)- The Apple TV 4K's built-in Siri microphone captures the room audio
- Music playing through speakers/HDMI is picked up and analyzed
Limitations:
- Requires Apple TV 4K (has built-in mic for Siri)
- Audio quality depends on room acoustics
- Background noise can affect analysis
- Slight latency (~50ms) between playback and visual response
Alternative approaches investigated:
AVAudioEnginetap onoutputNode— crashes because MusicKit owns the audio sessionMTAudioProcessingTap— requiresAVPlayer/AVPlayerItem, not compatible with MusicKitAVAudioPlayerNodewith MusicKit stream — MusicKit doesn't expose a stream URL
Fallback: When no microphone is available (e.g., Simulator), the app runs in simulated mode with procedurally generated audio data that produces plausible visualization behavior.
Full-screen Metal rendering at native resolution/refresh rate.
MTKViewas the primary rendering surface- Fragment shader per visual mode (fullscreen quad approach)
- Reads
AudioAnalysisDataevery frame via sharedUniformsstruct - ~1.5 second crossfade transition when switching modes
- Zero UI chrome during visualization — full bleed edge to edge
| # | Name | Character |
|---|---|---|
| 0 | C-240 | Lissajous curves, CRT-era colors, analog warmth. Homage to the original. |
| 1 | Membrane | Organic undulating surface, bass deformation, spectral color shifts |
| 2 | Particle Field | Thousands of particles, transient bursts, gravity wells, fading trails |
| 3 | Tunnel | Raymarched wormhole, amplitude-driven velocity, beat-synced color cycling |
| 4 | Glitch Grid | Hard-edged geometric grid, transient displacement, color inversion |
| 5 | Drift | Slow-evolving noise nebula, minimal audio reactivity, ambient background |
Every mode includes a drift parameter — slow autonomous evolution that prevents static-looking output even when audio is steady.
| Input | Action |
|---|---|
| Play/Pause button | Play/pause music |
| Left on clickpad | Previous track |
| Right on clickpad | Next track |
| Up on clickpad | Next visual mode |
| Down on clickpad | Previous visual mode |
| Menu button | Show/hide music browser |
| Select (center click) | Show Now Playing info briefly |
tvOS does not expose a public API for third-party screensaver registration. Instead, the app implements auto-dim simulation:
- After 30 seconds of no remote input, all UI is hidden
- The visualizer runs full-screen without any overlays
- If music is playing, it visualizes the music
- If no music is playing, the current mode runs with drift-only motion (ambient behavior)
- Any remote interaction immediately exits screensaver mode
VideoMusic/
├── VideoMusic.xcodeproj
├── VideoMusic.entitlements # MusicKit entitlement
├── VideoMusic/
│ ├── VideoMusic-Bridging-Header.h # Exposes ShaderTypes.h to Swift
│ ├── App/
│ │ ├── VideoMusicApp.swift # @main app entry point
│ │ └── ContentView.swift # Root view — orchestrates all systems
│ ├── Music/
│ │ ├── MusicManager.swift # MusicKit auth, library, playback
│ │ ├── MusicBrowserView.swift # Album/playlist/search browser UI
│ │ └── NowPlayingView.swift # Now Playing overlay (auto-fades)
│ ├── Audio/
│ │ ├── AudioEngine.swift # AVAudioEngine setup + mic loopback
│ │ ├── AudioAnalysisData.swift # Shared audio data (thread-safe)
│ │ └── FFTProcessor.swift # vDSP FFT + band extraction
│ ├── Visual/
│ │ ├── VisualRenderer.swift # MTKView + Metal pipeline
│ │ ├── ShaderTypes.h # Shared Swift/Metal data types
│ │ ├── ModeTransitionManager.swift # Crossfade between modes
│ │ └── Shaders/
│ │ ├── C240Mode.metal # Mode 0 + shared vertex shader
│ │ ├── MembraneMode.metal # Mode 1
│ │ ├── ParticleMode.metal # Mode 2
│ │ ├── TunnelMode.metal # Mode 3
│ │ ├── GlitchGridMode.metal # Mode 4
│ │ └── DriftMode.metal # Mode 5
│ ├── Remote/
│ │ └── RemoteInputHandler.swift # Siri Remote via GameController
│ └── Resources/
│ ├── Assets.xcassets
│ └── Info.plist
└── README.md
| Component | Technology |
|---|---|
| Language | Swift 5, Metal Shading Language |
| UI | SwiftUI |
| Music | MusicKit (ApplicationMusicPlayer) |
| Audio Analysis | AVFoundation, AVFAudio, Accelerate (vDSP) |
| Rendering | Metal, MetalKit |
| Input | GameController framework |
| Target | tvOS 17.0+ |
| Device | Apple TV 4K (all generations) |
- Open
VideoMusic.xcodeprojin Xcode 15+ - In the project's Signing & Capabilities:
- Set your Development Team
- The MusicKit capability should already be configured
- In the Apple Developer Portal:
- Register the App ID
com.fladrycreative.videomusic - Enable the MusicKit App Service
- Register the App ID
- Build and run on Apple TV Simulator or a real Apple TV 4K
- On first launch, approve the Apple Music access prompt
Note: The Simulator will run in simulated audio mode (no microphone). For real audio-reactive behavior, deploy to a physical Apple TV 4K.
The shaders are the soul of this app and will be iterated heavily. Here's what you need to know:
Audio → FFTProcessor → AudioAnalysisData → Uniforms (CPU) → Fragment Shader (GPU)
Every fragment shader receives a Uniforms struct containing:
- All 5 frequency bands + amplitude + transient + spectral centroid
- Time, delta time, resolution
- Current mode, transition progress
- Drift parameter (slow autonomous evolution)
- Each mode is a self-contained
.metalfile - The shared vertex shader lives in
C240Mode.metal ShaderTypes.hdefines theUniformsstruct — shared between Swift and Metal- Fragment function naming convention:
fragment[ModeName](e.g.,fragmentC240) - All modes receive the same
Uniformsdata — the difference is how they interpret it
- Create
NewMode.metalinVisual/Shaders/ - Add the fragment function (follow existing patterns)
- Add the fragment name to the
fragmentNamesarray inVisualRenderer.swift - Increment
modeCountinModeTransitionManager.swift - Add the mode name to
modeNamesarray - Add the enum case in
ShaderTypes.h
- Companion iOS/watchOS app
- OSC / network / external control
- Settings screen or preferences
- Social features, sharing, or recording
- In-app purchases or monetization
Copyright 2026 Fladry Creative. All rights reserved.