Skip to content

Instantly share code, notes, and snippets.

@sethkontny
Created October 16, 2014 00:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sethkontny/1ad4cea7f711e3eee763 to your computer and use it in GitHub Desktop.
Save sethkontny/1ad4cea7f711e3eee763 to your computer and use it in GitHub Desktop.
A Pen by Roshene.
#container
#warning1.message
%h1
This experiment requires the
%a{ href:'https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html', target:'_blank' } Web Audio API
%h2
Please try in one of
%a{ href:'http://caniuse.com/#feat=audio-api', target:'_blank' } these browsers
#warning2.message
%h1
Safari users. You may hear audio but see no visuals. This is due to
%a{ href:'http://goo.gl/6WLx1', target:'_blank' } this bug
in Safari 6
#intro.message
%h1 Simple music visualiser
%h2 Loading audio…
###
Music is by The XX
@see http://thexx.info
This is best viewed in Chrome since there is a bug in Safari
when using getByteFrequencyData with MediaElementAudioSource
@see http://goo.gl/6WLx1
###
# Config
NUM_PARTICLES = 150
NUM_BANDS = 128
SMOOTHING = 0.5
MP3_PATH = 'http://s.cdpn.io/1715/the_xx_-_intro.mp3'
SCALE = MIN: 5.0, MAX: 80.0
SPEED = MIN: 0.2, MAX: 1.0
ALPHA = MIN: 0.8, MAX: 0.9
SPIN = MIN: 0.001, MAX: 0.005
SIZE = MIN: 0.5, MAX: 1.25
COLORS = [
'#69D2E7'
'#1B676B'
'#BEF202'
'#EBE54D'
'#00CDAC'
'#1693A5'
'#F9D423'
'#FF4E50'
'#E7204E'
'#0CCABA'
'#FF006F'
]
# Audio Analyser
class AudioAnalyser
@AudioContext: self.AudioContext or self.webkitAudioContext
@enabled: @AudioContext?
constructor: ( @audio = new Audio(), @numBands = 256, @smoothing = 0.3 ) ->
# construct audio object
if typeof @audio is 'string'
src = @audio
@audio = new Audio()
@audio.controls = yes
@audio.src = src
# setup audio context and nodes
@context = new AudioAnalyser.AudioContext()
# JavaScriptNode so we can hook onto updates
@jsNode = @context.createJavaScriptNode 2048, 1, 1
# smoothed analyser with n bins for frequency-domain analysis
@analyser = @context.createAnalyser()
@analyser.smoothingTimeConstant = @smoothing
@analyser.fftSize = @numBands * 2
# persistant bands array
@bands = new Uint8Array @analyser.frequencyBinCount
# circumvent http://crbug.com/112368
@audio.addEventListener 'canplay', =>
# media source
@source = @context.createMediaElementSource @audio
# wire up nodes
@source.connect @analyser
@analyser.connect @jsNode
@jsNode.connect @context.destination
@source.connect @context.destination
# update each time the JavaScriptNode is called
@jsNode.onaudioprocess = =>
# retreive the data from the first channel
@analyser.getByteFrequencyData @bands
# fire callback
@onUpdate? @bands if not @audio.paused
start: ->
@audio.play()
stop: ->
@audio.pause()
# Particle
class Particle
constructor: ( @x = 0, @y = 0 ) ->
@reset()
reset: ->
@level = 1 + floor random 4
@scale = random SCALE.MIN, SCALE.MAX
@alpha = random ALPHA.MIN, ALPHA.MAX
@speed = random SPEED.MIN, SPEED.MAX
@color = random COLORS
@size = random SIZE.MIN, SIZE.MAX
@spin = random SPIN.MAX, SPIN.MAX
@band = floor random NUM_BANDS
if random() < 0.5 then @spin = -@spin
@smoothedScale = 0.0
@smoothedAlpha = 0.0
@decayScale = 0.0
@decayAlpha = 0.0
@rotation = random TWO_PI
@energy = 0.0
move: ->
@rotation += @spin
@y -= @speed * @level
draw: ( ctx ) ->
power = exp @energy
scale = @scale * power
alpha = @alpha * @energy * 1.5
@decayScale = max @decayScale, scale
@decayAlpha = max @decayAlpha, alpha
@smoothedScale += ( @decayScale - @smoothedScale ) * 0.3
@smoothedAlpha += ( @decayAlpha - @smoothedAlpha ) * 0.3
@decayScale *= 0.985
@decayAlpha *= 0.975
ctx.save()
ctx.beginPath()
ctx.translate @x + cos( @rotation * @speed ) * 250, @y
ctx.rotate @rotation
ctx.scale @smoothedScale * @level, @smoothedScale * @level
ctx.moveTo @size * 0.5, 0
ctx.lineTo @size * -0.5, 0
ctx.lineWidth = 1
ctx.lineCap = 'round'
ctx.globalAlpha = @smoothedAlpha / @level
ctx.strokeStyle = @color
ctx.stroke()
ctx.restore()
# Sketch
Sketch.create
particles: []
setup: ->
# generate some particles
for i in [0..NUM_PARTICLES-1] by 1
x = random @width
y = random @height * 2
particle = new Particle x, y
particle.energy = random particle.band / 256
@particles.push particle
if AudioAnalyser.enabled
try
# setup the audio analyser
analyser = new AudioAnalyser MP3_PATH, NUM_BANDS, SMOOTHING
# update particles based on fft transformed audio frequencies
analyser.onUpdate = ( bands ) => particle.energy = bands[ particle.band ] / 256 for particle in @particles
# start as soon as the audio is buffered
analyser.start();
# show audio controls
document.body.appendChild analyser.audio
intro = document.getElementById 'intro'
intro.style.display = 'none'
# bug in Safari 6 when using getByteFrequencyData with MediaElementAudioSource
# @see http://goo.gl/6WLx1
if /Safari/.test( navigator.userAgent ) and not /Chrome/.test( navigator.userAgent )
warning = document.getElementById 'warning2'
warning.style.display = 'block'
catch error
else
# Web Audio API not detected
warning = document.getElementById 'warning1'
warning.style.display = 'block'
draw: ->
@globalCompositeOperation = 'lighter'
for particle in @particles
# recycle particles
if particle.y < -particle.size * particle.level * particle.scale * 2
particle.reset();
particle.x = random @width
particle.y = @height + particle.size * particle.scale * particle.level
particle.move()
particle.draw @

Simple Audio Visualisation

A basic example of using the Web Audio API AudioContext with an AnalyserNode and frequency-domain analysis (FFT) to create a simple music visualiser.

A Pen by Roshene on CodePen.

License.

@import "compass/css3"
@import url( http://fonts.googleapis.com/css?family=Lato:400,700 )
html, body
font-family: 'Lato', sans-serif
background: #13242f
overflow: hidden
#container
&:before
@include background-image( radial-gradient( center, ellipse cover, rgba(0,0,0,0) 20%, rgba(0,0,0,1) 95% ) )
position: absolute
content: ''
z-index: 0
opacity: 0.9
height: 100%
width: 100%
left: 0
top: 0
&:after
background: url( 'http://s.cdpn.io/1715/noise-1.png' )
position: absolute
content: ''
z-index: 1
opacity: 0.8
height: 100%
width: 100%
left: 0
top: 0
audio
position: absolute
z-index: 2
right: 0
top: 0
.message
$height: 60px
$width: 360px
box-shadow: 0 2px 4px rgba(0,0,0,0.2)
text-transform: uppercase
border-radius: 3px
text-align: center
line-height: 1.2
background: rgba(0,0,0,0.8)
position: absolute
margin-left: $width * -0.5
margin-top: $height * -0.5
font-size: 13px
padding: 20px
display: none
z-index: 3
height: $height
width: $width
color: #fff
left: 50%
top: 50%
h1, h2
font-weight: 300
margin: 10px 0
a
text-decoration: none
font-weight: 700
color: #1B676B
#intro
display: block
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment