Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

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

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

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:'https://goo.gl/6WLx1', target:'_blank' } this bug
    in Safari 6

#intro.message

  %h1 Simple music visualiser
  %h2 Loading audio&hellip;
              
            
!

CSS

              
                @import compass

@import url( https://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( 'https://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 https://goo.gl/6WLx1

###

# Config

NUM_PARTICLES = 150
NUM_BANDS = 128
SMOOTHING = 0.5
MP3_PATH = 'https://api.soundcloud.com/tracks/42328219/stream?client_id=b1495e39071bd7081a74093816f77ddb'

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

Console