Skip to content

Instantly share code, notes, and snippets.

@LeetCodes
Created September 17, 2018 07:09
Show Gist options
  • Select an option

  • Save LeetCodes/f925defe818d48266db8dce4095e6178 to your computer and use it in GitHub Desktop.

Select an option

Save LeetCodes/f925defe818d48266db8dce4095e6178 to your computer and use it in GitHub Desktop.
Canvas and Audio Animation

Canvas and Audio Animation

Playing with Web Audio API AudioContext- the adjustments I made were mostly visual- using the canvas element to draw more complex shapes, changing colors, and using my friend @sudara's music delivered on cloudfront.

Built on the shoulders of Giants: Forked from Justin Windle's Pen Simple Audio Visualisation.

A Pen by Leetcodes on CodePen.

License.

#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:'https://goo.gl/6WLx1', target:'_blank' } this bug
in Safari 6
#intro.message
%h1 Simple music visualiser
%h2 Loading audio…
###
This is best viewed in Chrome since there is a bug in Safari
when using getByteFrequencyData with MediaElementAudioSource
@see https://goo.gl/6WLx1
###
# Config
NUM_PARTICLES = 150
NUM_BANDS = 128
SMOOTHING = 0.5
MP3_PATH = 'https://s3.us-east-2.amazonaws.com/leetcodes/assets/My_House_-_Flo_Rida_Jack_Dyer_Bootleg.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 = [
'#060719'
'#4C1B2F'
'#9E332E'
'#EBE54D'
'#EB6528'
'#FC9D1C'
'#B59D1C'
'#FF4E50'
'#FC9D5D'
'#1f6a83'
'#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
@audio.crossOrigin = "anonymous"
# setup audio context and nodes
@context = new AudioAnalyser.AudioContext()
# createScriptProcessor so we can hook onto updates
@jsNode = @context.createScriptProcessor 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 ) * 1
@smoothedAlpha += ( @decayAlpha - @smoothedAlpha ) * 1
@decayScale *= 7
@decayAlpha *= 0.975
ctx.save()
ctx.beginPath()
ctx.translate @x + cos( @rotation * @speed ) * 250, @y
ctx.rotate @rotation
ctx.scale @smoothedScale * @level, @smoothedScale * @level
##start to draw
## layer1/Path
ctx.stroke(new Path2D("M330.3,102.61L234,198.91l70.71,70.71,89.42-89.42,119,119-22.63,22.63-97.4-97.4-66.79,66.79,91.48,91.48L298.3,502.22l-21-21L375,383.45,304.56,313l-91.22,91.22L94,284.82l22.24-22.24L214,360.35l69-69-92-92L309.08,81.3l21.26,21.26Z"));
###
ctx.beginPath()
ctx.moveTo(102.0, 9.2)
ctx.lineTo(97.5, 12.7)
ctx.lineTo(92.2, 10.6)
ctx.lineTo(91.4, 4.9)
ctx.lineTo(95.9, 1.3)
ctx.lineTo(101.3, 3.5)
ctx.lineTo(102.0, 9.2)
ctx.closePath()
ctx.stroke()
###
###
ctx.beginPath()
ctx.moveTo(300, 0)
ctx.lineTo(250, 100)
ctx.lineTo(350, 100)
ctx.closePath()
ctx.stroke()
###
ctx.restore()
## end drawing
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 / 500
@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 https://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 @
<script src="https://soulwire.github.io/sketch.js/js/sketch.min.js"></script>
html, body {
overflow: hidden;
background: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/sound3.jpg');
background-size: cover;
width: 100%;
height: 100%;
}
#container{
&:before {
position: absolute;
content: '';
z-index: 0;
opacity: 0.9;
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