ASA Player – ASCII Music Visualizer
2025Retro music player with a real-time 140×16 ASCII spectrum analyzer, adaptive quality system, VU meters, oscilloscope, and glitch visual effects — all built with Web Audio API and Next.js.
ASA Player is a retro-styled music player that renders real-time audio analysis as an ASCII spectrum on a 140×16 character grid, inspired by vintage hardware analyzers. It streams tracks from a record label catalog, analyzes frequency data with the Web Audio API, and maps FFT bins to ASCII characters logarithmically — matching how human hearing actually perceives pitch. Five distinct visualization modes share one AudioContext, with a glitch aesthetic overlay that runs independently from the audio loop.
Existing web music players display either no visualizer or generic canvas-based bar charts that all look the same. The goal was to build a player with a strong, distinctive aesthetic — retro ASCII art combined with real-time audio analysis — without sacrificing technical correctness. Frequency visualization on the web typically uses linear frequency mapping, which wastes column space on inaudible high frequencies and compresses the musically important bass range. The project needed a solution that matched human hearing perception.
Build a production-quality web music player that: (1) renders a live ASCII spectrum analyzer with logarithmic frequency mapping; (2) supports multiple visualization modes from a single audio graph; (3) degrades gracefully on lower-end devices via an adaptive quality system; (4) maintains a cohesive retro glitch aesthetic without sacrificing readability or performance.
- ◈Solo developer — audio engineering, visualization design, frontend, deployment
- 01Logarithmic frequency mapping — 140 ASCII columns are mapped to 20Hz–22kHz using log10 scaling. Each column's FFT bin index is computed as pow(10, logMin + (logRange × i) / (cols − 1)). This matches the musical scale (each octave gets equal visual space) and is how analog hardware analyzers work. A linear mapping would dedicate 75% of columns to frequencies above 5kHz that carry little musical information.
- 02Adaptive quality system — 4 quality levels (ultra/high/medium/low) change the FFT size (512 → 64 bins) and visualization frame rate (20fps → 4fps). FPS is monitored with a rolling 10-sample history; auto-downgrade triggers at <30fps. This keeps the app usable on mid-range devices without a single hardcoded quality setting.
- 03Separate glitch layer with AdditiveBlending — the scanlines, RGB shift, and static noise effects run in a dedicated component (glitch-noise-overlay.tsx) completely decoupled from the audio render loop. Using CSS mix-blend-difference for the RGB shift means the glitch layer composites correctly over any background color without per-pixel JavaScript calculations.
- 04Dynamic import with loading skeletons — each of the 5 visualization components is code-split via Next.js dynamic(). Initial JS bundle is minimal; the heavy Three.js circular visualizer only loads if the user switches to that mode. Each component shows a themed ASCII loading skeleton during import.
- 05Uint8Array throughout — Web Audio API's getByteFrequencyData() returns Uint8Array natively. The entire frequency data pipeline keeps this type (zero-copy from browser to visualization), avoiding GC pressure from array allocations on every animation frame.
- 06crossOrigin='anonymous' on the audio element — required for Web Audio API's createMediaElementSource() to access audio data from a cross-origin URL. Without this attribute, the AnalyserNode can connect but getByteFrequencyData() returns zeros due to CORS security restrictions.
- →Logarithmic frequency mapping is non-negotiable for musical visualizations — a linear mapping made bass frequencies invisible and high frequencies dominant. Switching to log scaling transformed the analyzer from a technically correct but musically useless display into something that actually feels like music.
- →Adaptive quality systems need real usage data to calibrate thresholds — the initial FPS thresholds were too aggressive, causing users on decent hardware to see unnecessary quality warnings. A 10-sample rolling average and a 5-second warning cooldown reduced false positives significantly.
- →Decoupling visual effects from audio processing prevents subtle timing bugs — early versions had the glitch overlay on the same render loop as the ASCII analyzer, causing glitch frames to drop when the FFT computation was expensive. Separating them eliminated the coupling.
- →Dynamic imports change the perceived load time significantly — adding loading skeletons for each visualization mode made the app feel instant even when Three.js was still downloading.
Live — deployed to GitHub Pages