Animating with CSS variables and Web Audio
We will learn to animate elements by utilising web audio and CSS variables
In this article i'll recreate a web animation I used for a friend of mine who is a musician. I created his online portfolio filled with web animation, more about that later!
Tech stack
- HTML for creating element to animate, etc.
- P5js to get 5 values from audio clips
- CSS Variables, just regular CSS while utilising CSS :root variables
Goal
We want to recreate the classic audio visualisation bars in a performant way.
Markup
First we create some bars. We want them to alternate in direction.
1<div class="half-height">2 <div class="divider">3 <div class="bar horizontal"></div>4 <div class="bar horizontal"></div>5 <div class="bar horizontal"></div>6 <div class="bar horizontal"></div>7 <div class="bar horizontal"></div>8 </div>9</div>10<div class="half-height">11 <div class="divider reverse">12 <div class="bar horizontal"></div>13 <div class="bar horizontal"></div>14 <div class="bar horizontal"></div>15 <div class="bar horizontal"></div>16 <div class="bar horizontal"></div>17 </div>18</div>
Don't forget to include the P5 library and add-ons you need
1<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.min.js"></script>2<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>3<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
CSS
Normal CSS!? For this example, just regular CSS was fine. I created some bars with a gradient background.
1.divider {2 width: 100%;3 display: flex;4 flex-direction: column;5 align-items: flex-end;6}7.divider.reverse {8 align-items: flex-start;9}10.divider .bar.horizontal {11 width: 50vw;12 height: 0.5rem;13 transition: all 60ms ease-in-out;14 background: linear-gradient(15 to left,16 #23074d,17 #6800ed18 ); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */19 transform-origin: right;20 opacity: 0.85;21}22.divider.reverse .bar.horizontal {23 transform-origin: left;24}2526.bar-container {27 display: flex;28 align-items: flex-end;29 height: 100vh;30}
CSS Variables
We defined the variables we need:
1:root {2 --mapBass: 0;3 --mapTremble: 0;4 --mapLowMid: 0;5 --mapMid: 0;6 --mapHighMid: 0;7}
Animating CSS variables
With JavaScript you can update CSS variables to certain values. These values can then be used in CSS like this:
1.bar.horizontal:nth-child(1) {2 transform: scaleX(var(--mapBass));3}4.bar.horizontal:nth-child(2) {5 transform: scaleX(var(--mapTremble));6}7.bar.horizontal:nth-child(3) {8 transform: scaleX(var(--mapLowMid));9}10.bar.horizontal:nth-child(4) {11 transform: scaleX(var(--mapMid));12}13.bar.horizontal:nth-child(5) {14 transform: scaleX(var(--mapHighMid));15}
Because we defined a transition on the bar, the bars will animate when the variables are updated.
Updating CSS variables with JavaScript
1this.root.style.setProperty('--mapBass', p.map(bass, 0, 255, 0, 2.0));2this.root.style.setProperty('--mapTremble', p.map(treble, 0, 255, 0, 2.0));3this.root.style.setProperty('--mapLowMid', p.map(lowMid, 0, 255, 0, 2.0));4this.root.style.setProperty('--mapMid', p.map(mid, 0, 255, 0, 2.0));5this.root.style.setProperty('--mapHighMid', p.map(highMid, 0, 255, 0, 2.0));
Gathering the audio values with P5js
1class Audio {2 constructor() {3 this.mapBass = 0;4 this.mapTremble = 0;5 this.mapMid = 0;6 this.mapLowMid = 0;7 this.mapHighMid = 0;8 this.bars = Array.from(document.querySelectorAll('.bar'));9 this.root = document.documentElement;10 }1112 init() {13 const s = p => {14 let audio, fft;15 // Load the soundfile16 p.preload = () => {17 audio = p.loadSound('/src/jerry-island-free.mp3');1819 // Play/pause button20 document.querySelector('#play').addEventListener('click', e => {21 e.target.remove();22 if (audio.isPlaying()) {23 audio.pause();24 } else {25 audio.loop();26 }27 });28 };2930 p.setup = () => {31 fft = new p5.FFT();32 };3334 p.draw = () => {35 fft.analyze();3637 // Get sound values38 const bass = fft.getEnergy('bass');39 const treble = fft.getEnergy('treble');40 const lowMid = fft.getEnergy('lowMid');41 const mid = fft.getEnergy('mid');42 const highMid = fft.getEnergy('highMid');4344 // Set CSS variables to a value between 0 and 245 this.root.style.setProperty('--mapBass', p.map(bass, 0, 255, 0, 2.0));46 this.root.style.setProperty(47 '--mapTremble',48 p.map(treble, 0, 255, 0, 2.0)49 );50 this.root.style.setProperty(51 '--mapLowMid',52 p.map(lowMid, 0, 255, 0, 2.0)53 );54 this.root.style.setProperty('--mapMid', p.map(mid, 0, 255, 0, 2.0));55 this.root.style.setProperty(56 '--mapHighMid',57 p.map(highMid, 0, 255, 0, 2.0)58 );59 };60 };61 new p5(s);62 }63}
Then start the program like this:
1const audio = new Audio();2audio.init();
Demo
And we're done! Have a look at the demo to see how it all comes together.
PS.
Next week there will be an article about converting someones head to particles and then animating those particles to audio values. Sounds cool, right?
Thanks for reading!
I hope someone somewhere learned something via this post! If you did, please consider sharing the article.
Other posts you might like
Accessible Wavy Text Animation using React Hooks and Framer Motion
February 17, 2021In this article i'll recreate a wavy text animation we built with React Hooks and GSAP v3 but…
How we drove up sales with animation and storytelling
October 05, 2020“Good animation is invisible. You shouldn’t notice that you’re looking at animation. You want to…
Transforming an image to animating Particles with Web Audio
August 19, 2020At my company Level30Wizards , we experiment with web techniques to learn how we can implement…