Theme Toggler with Audio

by Hannah Goodridge ~ 4 min read

Like this post?

00

I've always loved adding little touches of personality to my projects. When I was working on my site's theme toggle, I realised it was pretty boring - just a basic switch between light and dark modes. So I decided to spice it up a bit.

The concept was straightforward: what if instead of a dull toggle, we had something that felt more like an experience? I wanted to add:

  • A rotating dial animation that shows sun and moon icons
  • Audio feedback that changes based on the direction
  • A satisfying click sound for every interaction

The Audio Experience

🌅 Sunrise (Dark → Light)

When switching to light mode, you get:

  • Click sound: A sharp, satisfying click (4000Hz sawtooth wave)
  • Sunrise chorus: An ascending chord progression that feels uplifting and celebratory

🌇 Sunset (Light → Dark)

When switching to dark mode, you get:

  • Click sound: Same satisfying click sound
  • Sunset sound: A descending chord progression - the exact reverse of sunrise

The symmetry creates a beautiful day/night cycle where the sun rises with an ascending chord and sets with a descending chord!

Note: The CodePen demo uses A major chords, while my personal site implementation uses C major chords for a slightly different musical character.

The Technical Implementation

Web Audio API

All sounds are generated programmatically using the Web Audio API:

const playSunriseChorus = () => { const frequencies = [523, 659, 784, 1047]; // C, E, G, C (C major chord) for (let i = 0; i < frequencies.length; i++) { const osc = audioContext.createOscillator(); const gain = audioContext.createGain(); osc.frequency.setValueAtTime(frequencies[i], audioContext.currentTime); osc.type = 'sine'; const delay = i * 0.05; gain.gain.setValueAtTime(0, audioContext.currentTime + delay); gain.gain.linearRampToValueAtTime(0.1, audioContext.currentTime + delay + 0.1); gain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + delay + 0.8); osc.start(audioContext.currentTime + delay); osc.stop(audioContext.currentTime + delay + 0.8); } };

CSS Dial Animation

The dial uses CSS transforms and opacity transitions:

.rotating-dial { transform-origin: center center; transition: transform 0.5s ease-in-out; } .dial-light { transform: rotate(60deg); } .dial-dark { transform: rotate(-60deg); } .icon-sun, .icon-moon { opacity: 0.5; transition: opacity 0.5s ease-in-out; } .dial-light .icon-sun { opacity: 1; } .dial-dark .icon-moon { opacity: 1; }

Try It Yourself!

I've created a standalone version on CodePen that you can play with:

The demo includes:

  • Complete dial animation
  • All audio effects
  • Keyboard accessibility
  • Theme state display
  • Responsive design

You can also experience this theme toggle right here on my site - it's the dial-shaped button in the navigation bar! I've used slightly different audio effects and animations, but the core experience is the same.

Why Audio Feedback?

Adding audio to UI interactions might seem like a small detail, but it can significantly enhance the user experience:

  1. Immediate feedback: Users know their action was registered
  2. Emotional connection: The sunrise/sunset sounds create a narrative
  3. Accessibility: Audio cues can help users with visual impairments
  4. Delight: It's just fun! Small moments of joy make interfaces memorable

Browser Compatibility

The Web Audio API is well-supported in modern browsers, but the implementation includes graceful fallbacks:

  • Audio context creation with vendor prefixes
  • Error handling for unsupported browsers
  • Silent failure if audio is blocked

The Result

What started as a simple theme toggle became a delightful interactive experience. The combination of smooth animations, satisfying audio feedback, and the day/night metaphor creates something that's both functional and fun.

It's a reminder that even small interactions can be opportunities to add personality and joy to our interfaces. Sometimes the best features are the ones that make users smile!


Want to see more interactive experiments? Check out my playground for other fun projects, or explore the blog for more technical deep-dives.

Read next