<h2 class="message">PLAY</h2>
<a id="badge" href="http://www.chromeexperiments.com/experiment/audio-cloud/" target="_blank"><img src="data:image/png;base64,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" alt="See my Experiment on ChromeExperiments.com"></a>>
html, body {
margin:0;
overflow:hidden;
background:#000;
color:white;
font-family:Arial;
}
.message{
padding:20px;
position:absolute;
top:50%;
left:50%;
transform:translate(-50%, -50%);
background:black;
cursor:pointer;
font-size:14px;
border:1px solid white;
}
#badge{
position: absolute;
bottom: 0;
left: 0;
height: 92px;
}
a:link, a:hover, a:visited, a:active{
color:white;
text-decoration: none;
}
.experiment-url{
position:absolute;
bottom:10px;
right:10px;
padding:8px;
z-index: 1;
font-size: 11px;
background:black;
letter-spacing:0.5px;
border: 1px solid white;
}
AUDIO_URL = "https://ma77os-media-assets.s3.us-east-2.amazonaws.com/audio/paradise_circus.mp3"
modes = ["cubic", "conic"]
themes = {
pinkBlue:[0xFF0032, 0xFF5C00, 0x00FFB8, 0x53FF00]
yellowGreen:[0xF7F6AF, 0x9BD6A3, 0x4E8264, 0x1C2124, 0xD62822]
yellowRed:[0xECD078, 0xD95B43, 0xC02942, 0x542437, 0x53777A]
blueGray:[0x343838, 0x005F6B, 0x008C9E, 0x00B4CC, 0x00DFFC]
blackWhite:[0xFFFFFF, 0x000000, 0xFFFFFF, 0x000000, 0xFFFFFF]
}
themesNames = []
for k, v of themes
themesNames.push k
# PARAMETERS
params = {
# public
mode: modes[0]
theme:themesNames[0]
radius: 3
distance: 600
size: .5
# private
numParticles: 5000
sizeW: 1
sizeH: 1
radiusParticle: 60
themeArr:themes[this.theme]
}
TOTAL_BANDS = 256
cp = new PIXI.Point()
mouseX = 0
mouseY = 0
mousePt = new PIXI.Point()
windowW = 0
windowH = 0
stage = null
renderer = null
texCircle = null
circlesContainer = null
arrCircles = []
hammertime = null
message = null
# audio
audio = null
analyser = null
analyserDataArray = null
isPlaying = false
canplay = false
# gui
gui = null
init = ->
initGestures()
message = $(".message")
message.on("click", play)
resize()
build()
resize()
mousePt.x = cp.x
mousePt.y = cp.y
$(window).resize(resize)
startAnimation()
initGUI()
play = ->
return if isPlaying
initAudio()
message.css("cursor", "default")
if canplay
message.hide()
else
message.html("LOADING MUSIC...")
audio.play()
isPlaying = true
initGUI = ->
gui = new dat.GUI()
# if window.innerWidth < 500
gui.close()
modeController = gui.add params, 'mode', modes
modeController.onChange (value) ->
changeMode value
themeController = gui.add(params, 'theme', themesNames)
themeController.onChange (value) ->
changeTheme params.theme
gui.add params, 'radius', 1, 8
gui.add params, 'distance', 100, 1000
sizeController = gui.add params, 'size', 0, 1
sizeController.onChange (value) ->
resize value
initAudio = ->
context = new (window.AudioContext || window.webkitAudioContext)()
analyser = context.createAnalyser()
# analyser.smoothingTimeConstant = 0.5
source = null
audio = new Audio()
audio.crossOrigin = "anonymous"
audio.src = AUDIO_URL
audio.addEventListener 'canplay', ->
if(isPlaying)
message.hide()
canplay = true
source = context.createMediaElementSource audio
source.connect analyser
source.connect context.destination
analyser.fftSize = TOTAL_BANDS * 2
bufferLength = analyser.frequencyBinCount
analyserDataArray = new Uint8Array bufferLength
startAnimation = ->
requestAnimFrame(update)
initGestures = ->
$(window).on 'mousemove touchmove', (e) ->
if e.type == 'mousemove'
mouseX = e.clientX
mouseY = e.clientY
else
mouseX = e.originalEvent.changedTouches[0].clientX
mouseY = e.originalEvent.changedTouches[0].clientY
build = ->
stage = new PIXI.Stage 0x000000
renderer = PIXI.autoDetectRenderer {
width: $(window).width()
height:$(window).height()
antialias:true
resolution:window.devicePixelRatio
}
$(document.body).append renderer.view
texCircle = createCircleTex()
buildCircles()
buildCircles = ->
circlesContainer = new PIXI.DisplayObjectContainer()
stage.addChild(circlesContainer)
for i in [0..params.numParticles-1]
circle = new PIXI.Sprite texCircle
circle.anchor.x = 0.5
circle.anchor.y = 0.5
circle.position.x = circle.xInit = cp.x
circle.position.y = circle.yInit = cp.y
circle.mouseRad = Math.random()
circlesContainer.addChild(circle)
arrCircles.push(circle)
changeTheme params.theme
createCircleTex = ->
gCircle = new PIXI.Graphics()
gCircle.beginFill(0xFFFFFF)
gCircle.drawCircle(0, 0, params.radiusParticle)
gCircle.endFill()
gCircle.generateTexture()
resize = ->
windowW = $(window).width()
windowH = $(window).height()
cp.x = windowW * .5
cp.y = windowH * .5
params.sizeW = windowH * params.size
params.sizeH = windowH * params.size
changeMode(params.mode)
if renderer
renderer.resize(windowW, windowH)
changeTheme = (name)->
params.themeArr = themes[name]
indexColor = 0
padColor = Math.ceil params.numParticles / params.themeArr.length
for i in [0..params.numParticles-1]
circle = arrCircles[i]
group = indexColor * padColor / params.numParticles
circle.blendMode = if params.theme == "blackWhite" then PIXI.blendModes.NORMAL else PIXI.blendModes.ADD
circle.indexBand = Math.round(group * (TOTAL_BANDS-56))-1
if circle.indexBand <= 0
circle.indexBand = 49
circle.s = (Math.random() + (params.themeArr.length-indexColor)*0.2)*0.1
circle.scale = new PIXI.Point(circle.s, circle.s)
if i % padColor == 0
indexColor++
circle.tint = params.themeArr[indexColor - 1]
changeMode = (value)->
return if !arrCircles || arrCircles.length == 0
# randomize mode if not specified
if !value
value = modes[Math.floor(Math.random()*modes.length)]
params.mode = value
for i in [0..params.numParticles-1]
circle = arrCircles[i]
switch params.mode
# cubic
when modes[0]
circle.xInit = cp.x + (Math.random() * params.sizeW - params.sizeW/2)
circle.yInit = cp.y + (Math.random() * params.sizeH - params.sizeH/2)
# circular
when modes[1]
angle = Math.random() * (Math.PI * 2)
circle.xInit = cp.x + (Math.cos(angle)*params.sizeW)
circle.yInit = cp.y + (Math.sin(angle)*params.sizeH)
update = ->
requestAnimFrame(update)
t = performance.now() / 60
if analyserDataArray && isPlaying
analyser.getByteFrequencyData analyserDataArray
if mouseX > 0 && mouseY > 0
mousePt.x += (mouseX - mousePt.x) * 0.03
mousePt.y += (mouseY - mousePt.y) * 0.03
else
a = t*0.05
mousePt.x = cp.x + Math.cos(a) * 100
mousePt.y = cp.y + Math.sin(a) * 100
for i in [0..params.numParticles-1]
circle = arrCircles[i]
if analyserDataArray && isPlaying
n = analyserDataArray[circle.indexBand]
scale = ((n / 256)) * circle.s*2
else
scale = circle.s*.1
scale *= params.radius
circle.scale.x += (scale - circle.scale.x) * 0.3
circle.scale.y = circle.scale.x
dx = mousePt.x - circle.xInit
dy = mousePt.y - circle.yInit
dist = Math.sqrt(dx * dx + dy * dy)
angle = Math.atan2(dy, dx)
r = circle.mouseRad * params.distance + 30
xpos = circle.xInit - Math.cos(angle) * r
ypos = circle.yInit - Math.sin(angle) * r
circle.position.x += (xpos - circle.position.x) * 0.1
circle.position.y += (ypos - circle.position.y) * 0.1
renderer.render(stage)
init()
View Compiled
This Pen doesn't use any external CSS resources.