octocatstartv

Pen Settings

via HTML Inspector

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource
via CSS Lint

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource
via JS Hint

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Want a Run Button?

If active, the preview will update automatically when you change code.

HTML

            
              
#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…
            
          
!

CSS

            
              @import compass

@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
            
          
!

JS

            
              ###

  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://crossorigin.me/https://s3-us-west-2.amazonaws.com/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.crossOrigin = "anonymous"
      @audio.controls = yes
      @audio.src = src
  
    # 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 ) * 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 @
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console