Paper Display: Building a Music Player That Feels Like a Paper Desk Object
Most music players try to be Spotify. This one was built to feel like something sitting quietly on your desk.
The idea was simple: upload a local audio file and have it display like a printed paper artifact — not a streaming dashboard, not a visualiser, not a media player. A calm, paper-textured desk object that shows your song and plays it. Getting there took three versions, a design tool, a tonearm needle, and more browser sync problems than expected.
Where this started
The starting prompt was specific about what to avoid.
No nightclub visualisers. No neon. No glassmorphism. No full music player. The goal was something that felt like a Kindle — calm, readable, unhurried — but for music. Upload a local audio file, and the display should feel like it is printing itself: title appearing, record fading in, progress line drawing, paper grain underneath.
The app was built around a small set of controls: paper mood, paper format, paper color, and reaction style. Those options let you shape how the display looks and behaves without turning the thing into a settings panel. The core workflow was always: upload audio, play, adjust the paper feel, leave it running.
OpenClaude Design was used for design work alongside the build. Having a design layer separate from the code helped keep the aesthetic direction clear, especially when the code was changing quickly between versions.
What went well
The paper-object direction got stronger with each version. What started as a loose concept — "make it feel like paper" — became a genuinely distinct visual identity by the end. The desk-object aesthetic held up.
The core workflow stayed intact across all three versions. Upload local audio, see it displayed, play it, change the paper options. Nothing important got broken or removed while iterating on the design.
The later version added useful structural ideas: a larger central paper display, a collapsible controls tray, a waveform strip, a loaded tracks list, reaction preview, and intensity and sensitivity controls. None of those were in the first version. They came from seeing what was missing in practice.
The Needle reaction ended up working well. It is a real tonearm that pivots while music plays and rests when it stops. That was the right call — it adds movement that is connected to the music, not just decoration. Getting it to feel like a record-player stylus rather than an animated prop took tuning, but the final version is close to what was wanted.
The adaptive title sizing also solved a real problem. Long song titles were overpowering the layout. The fix — sizing the title based on length, with an overflow check that drops it to a compact class — kept the editorial paper feel without the title dominating the whole display.
What did not go well
The most basic problem happened early: edits were going to the wrong file.
The project had two paths — a React/CSS version and a standalone index.html. Changes were being applied to the React path. The browser was opening the standalone file. Nothing updated. This wasted time and was confusing until it got diagnosed.
Folder names were also assumed wrong. The actual folders were paper-display (1) and paper-display (3). The build was looking for different names. Small thing, real friction.
Some imported designs looked finished visually but had broken or incomplete behavior when dropped into the live standalone file. A design that looks complete in a static prototype is not the same as a design that works in the actual running app. That gap had to be closed manually each time.
Reaction styles were selectable in the UI but did not clearly change the main display for a while. The preview updated. The main display did not. That kind of disconnect is frustrating because the feature looks like it is working right up until you need it to.
What went bad
Audio upload broke in the third version. Tracks were being added to a list but not selected or played immediately. You could upload a file, see it in the list, and nothing would happen. The wiring between upload and playback had come apart somewhere in the design iteration.
The right-hand controls panel had a fixed height. It cut off the bottom of the controls. A layout decision that works in a static design comp stops working the moment the content is taller than expected.
The animation system was tied too closely to playback and analyser state. When those conditions were not exactly right — which happened more than expected — animations behaved unpredictably. Some played when music stopped. Some did not play when it started. The dependency chain was fragile.
The Needle reaction was too exaggerated in the first pass. It moved dramatically, looked more like a visualiser element than a stylus, and broke the quiet desk-object feeling the whole build was working toward. It had to be pulled back significantly.
What needed a lot of fixing
Making sure the right file was being edited and that the browser was reflecting those edits. That should not take time. It did.
Wiring the upload so it selects and plays the uploaded track immediately. Not adds it to a list. Plays it.
Reusing the Web Audio analyser safely. Browsers reject attempts to create a new analyser from an already-connected audio context. The fix was to reuse the existing one instead of recreating it. Once that was handled, the audio-driven animations stabilised.
Making the Reaction Style selection affect the main display, not just the preview. The preview showing the right thing while the main display ignored the selection was a real confidence problem during testing.
Making animations stop when music stops. Simple requirement. Required fixing the playback state detection so animations were not running on stale state.
Tuning the Needle. The final version pivots gently, responds to music, and rests when the track stops. Getting there meant reducing the range of motion, slowing the sensitivity, and removing the behaviour that made it look like it was dancing.
The prompts that shaped the direction
The first prompt set the whole direction. It defined what the tool was not allowed to become just as clearly as what it should be.
Improve the current Paper Jukebox interface so it feels less like a web music player and more like a finished paper desk object.
Do not turn it into a full music player.
Keep the design calm, quiet, and readable. Avoid neon, glassmorphism, nightclub visualisers, or over-animated effects.
The final result should feel like: “A Kindle-like paper music companion that turns a local song into a living printed desk card.”
That constraint — "do not turn it into a full music player" — was the most important line in the whole prompt. It stopped the build from drifting toward playlists, libraries, equalizers, and all the other features that music apps accumulate. Paper Display stayed small because that line held.
The third prompt tackled the title layout problem specifically.
Long song titles become too large and overpower the layout. Keep short titles large and dramatic. Reduce the title size for medium, long, and very long titles.
Treat the lower song information area like a printed archive card. Add a small uppercase label row above the title. Keep the title inside a fixed visual area so it cannot dominate the whole page.
The final result should feel like a premium printed music artifact, not a normal web audio player.
Naming the visual reference — "printed archive card," "Kindle-like paper companion" — gave the AI something to aim at that went beyond technical requirements. The design improved when the prompt described how the thing should feel, not just what it should do.
What this build teaches
The wrong file problem is easy to prevent and easy to miss. When a project has multiple versions or paths — a React build and a standalone file, old folders and new ones — check which file the browser is actually loading before spending any time on edits. One print statement or console log at the top of the file is enough to confirm it.
Design imports are not free. A design that looks finished in a prototype still has to be wired into the actual running app. Behavior that works in a static comp does not transfer automatically. Budget time for closing that gap on every import.
Aesthetic constraints in prompts work. "Do not turn this into a full music player" did more for this build than any technical requirement. If the constraint is clear, the AI tends to respect it. If the constraint is vague, the build drifts toward the most common version of the thing — which for music players means Spotify.
Audio browser APIs need careful handling. The Web Audio API in particular has rules about context creation and analyser reuse that will quietly break things if you recreate objects that should be reused. Read the error. It usually tells you exactly what the browser rejected.
Small reactions are better than large ones. The Needle was more convincing once it stopped moving dramatically. Subtle, music-driven, physically plausible — that is the version that fits a desk-object design. The first version looked like it was performing. The fixed version looks like it is working.
What the app does now
- Upload a local audio file and play it immediately
- Song title, artist, album, progress, and time displayed as a paper artifact
- Paper mood, format, and color options to adjust the display feel
- Reaction style selection that changes how the display responds to music
- Needle tonearm that pivots while music plays and rests when it stops
- Adaptive title sizing so long titles do not overpower the layout
- Loaded tracks list for the current session
- Waveform strip and intensity controls
- Animations that stop when music stops
No accounts. No streaming. No backend. Everything runs locally in the browser from a single HTML file.