DEV Community

Cover image for I Wanted My Pet’s Photo to Dance to the Beat, So I Built a Music Visualizer
Yana Li
Yana Li

Posted on

I Wanted My Pet’s Photo to Dance to the Beat, So I Built a Music Visualizer

DEV Weekend Challenge: Passion Edition Submission

This is a submission for Weekend Challenge: Passion Edition.

Disclosure: AI coding and editing tools helped me build and review this weekend project. The demo, screenshots, tests, and product behavior below are from the working app.

I wanted to see whether a still photo could move with music without turning it into a fake talking face. More specifically: could I make a pet photo dance to the beat?

For DEV's Weekend Challenge, I gave myself one weekend to build a music visualizer around that question.

I am working toward a bigger idea: personalized songs and videos for the people and pets we love.

But there was an uncomfortable product question underneath it: if the visual moment is not worth replaying, why build the rest of the pipeline?

So for this weekend I cut the idea down to one photo, one song, and one button.

What I Built

PostLullaby is a browser-only music visualizer for personal photos. Choose a JPEG, PNG, or WebP photo, pick a track, and press Enter the beat. The original photo dissolves into a particle portrait made from its own pixels, then dances to the music through breathing, waves, flashes, and shock rings before returning to the untouched photo.

PostLullaby music visualizer before a photo is selected

There are three ways to add a pulse:

  • Original Spark: a 15-second test track made for the prototype.
  • Energetic Dance Pop: a 30-second royalty-free preview for a stronger drum test.
  • Your Song: a browser-supported audio file from your own device.

The last option matters to me. Your selected photo and local song stay in your browser as local object URLs. There is no account, upload, database, analytics event, or server-side media processing in this prototype.

It is not pretending to animate a face. It is the real photo, temporarily redrawn as light.

Demo

Try PostLullaby live — use your pet, your dad, your partner, or anyone who deserves a little main-character energy.

And this is the moment I built the whole spike to test: the dog is still recognizable, but the photo has become a field of particles responding to the song.

PostLullaby turning a dog portrait into a beat-reactive particle music visualizer

A static frame only catches one beat. The live version has sound and pointer interaction, so move your mouse or finger through the portrait while it plays.

For the before-and-after demo images, I used a photo by Kris Tian on Unsplash, available under the Unsplash License. The bundled library track is “Energetic Dance Pop” by Tunetank under the Pixabay Content License; the source and license are recorded in the repository.

Code

🔗 View the source code on GitHub

Live app: postlullaby.vercel.app

How I Built It

The photo becomes particles without becoming fake

When a user selects a photo, the browser decodes the local File with createImageBitmap(). I downsample it once to a small working canvas, read the pixel data, and build a deterministic set of particles.

Each particle remembers:

  • its home position in the photo;
  • its real sampled color and luminance;
  • a small depth value for perspective;
  • spring velocity and temporary beat displacement.

That gives me a recognizable portrait without generating a new face or inventing motion for the subject. On desktop the renderer uses a denser grid; mobile uses fewer columns to keep the effect responsive.

The beat detector had to learn each track’s personality

The audio path is intentionally small: one HTMLAudioElement feeds one AudioContext, one AnalyserNode, and one animation loop. Every frame I read three energy bands:

const low = averageRange(20, 140);
const mid = averageRange(140, 2_000);
const high = averageRange(2_000, 12_000);

const { beat, intensity } = detector.sample(
  low,
  performance.now(),
);
Enter fullscreen mode Exit fullscreen mode

My first versions treated a beat as “energy above a threshold.” That worked for one clean synthetic kick and failed on a loud, compressed pop track whose low-end energy never dropped very far.

The fix was to anchor detection to the track’s own recent range. The detector keeps a 900ms low-energy window, measures its local minimum and maximum, looks for a fast onset rise, enforces a cooldown, and only rearms after the energy falls enough relative to that same local range.

That was the least glamorous part of the build and the part that made it feel most like music instead of random flashing.

I choreographed a moment, not a screensaver

The visual runs as a short sequence:

  1. The untouched photo settles on the stage.
  2. The first strong beat dissolves it into its own particle colors.
  3. Low frequencies drive breathing, mids drive waves and brightness, and detected beats launch flashes and shock rings.
  4. Near the end, the particles return home and the original photo comes back.

Pause freezes the current frame instead of clearing the canvas. Replay resets the beat detector and choreography cleanly. prefers-reduced-motion keeps the photo visible and swaps the particle movement for a gentler glow.

What I deliberately did not build

The larger PostLullaby idea includes memory prompts, original song generation, export, and sharing. None of that is in this submission.

That is deliberate. A weekend is enough time to validate one risky product question, but not enough time to make an unfinished pipeline look trustworthy. This build asks only:

Does one photo hitting the beat create a moment you want to replay?

For the release check, 25 focused tests passed alongside lint, TypeScript, and the production build. I ran the production build at desktop and mobile sizes, including reduced-motion mode, then verified a real photo upload and playback against the public Vercel URL.

What I Learned

The fun part was not adding more technology. It was making the first three seconds understandable.

The photo stays yours. The music drives something visible. The button does exactly what it says.

If you try it, I would love to know which image you chose. A pet is the obvious answer. A classic photo of your dad is a very good answer. Your partner, your best friend, or a gloriously overconfident childhood photo all count too.

Pick one photo. Pick one song. Watch it dance to the beat — then tell me if you replayed it.

Top comments (0)