ThreeJS/WebGL Soundcloud player/visualizer based on HTML5 AudioContext API, see JS comments for full list of credits. PS: Doesn't load on the preview grid
A Pen by Luigi Mannoni on CodePen.
audio#player(controls='', autoplay='', preload='', autobuffer='') | |
#warning | |
| Your browser doesn't support HTML5 AudioContext API, this demo won't run properly. Please use latest Chrome or Firefox on a desktop device | |
script#fragmentNoise(type='x-shader/x-fragment'). | |
// | |
// Description : Array and textureless GLSL 3D simplex noise function. | |
// Author : Ian McEwan, Ashima Arts. | |
// Maintainer : ijm | |
// Lastmod : 20110409 (stegu) | |
// License : Copyright (C) 2011 Ashima Arts. All rights reserved. | |
// Distributed under the MIT License. See LICENSE file. | |
// | |
uniform float time; | |
varying vec3 vTexCoord3D; | |
varying vec3 vNormal; | |
varying vec3 vViewPosition; | |
vec4 permute( vec4 x ) { | |
return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 ); | |
} | |
vec4 taylorInvSqrt( vec4 r ) { | |
return 1.79284291400159 - 0.85373472095314 * r; | |
} | |
float snoise( vec3 v ) { | |
const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 ); | |
const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 ); | |
// First corner | |
vec3 i = floor( v + dot( v, C.yyy ) ); | |
vec3 x0 = v - i + dot( i, C.xxx ); | |
// Other corners | |
vec3 g = step( x0.yzx, x0.xyz ); | |
vec3 l = 1.0 - g; | |
vec3 i1 = min( g.xyz, l.zxy ); | |
vec3 i2 = max( g.xyz, l.zxy ); | |
// x0 = x0 - 0. + 0.0 * C | |
vec3 x1 = x0 - i1 + 1.0 * C.xxx; | |
vec3 x2 = x0 - i2 + 2.0 * C.xxx; | |
vec3 x3 = x0 - 1. + 3.0 * C.xxx; | |
// Permutations | |
i = mod( i, 289.0 ); | |
vec4 p = permute( permute( permute( | |
i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) | |
+ i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) | |
+ i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) ); | |
// Gradients | |
// ( N*N points uniformly over a square, mapped onto an octahedron.) | |
float n_ = 1.0 / 7.0; // N=7 | |
vec3 ns = n_ * D.wyz - D.xzx; | |
vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N) | |
vec4 x_ = floor( j * ns.z ); | |
vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N) | |
vec4 x = x_ *ns.x + ns.yyyy; | |
vec4 y = y_ *ns.x + ns.yyyy; | |
vec4 h = 1.0 - abs( x ) - abs( y ); | |
vec4 b0 = vec4( x.xy, y.xy ); | |
vec4 b1 = vec4( x.zw, y.zw ); | |
vec4 s0 = floor( b0 ) * 2.0 + 1.0; | |
vec4 s1 = floor( b1 ) * 2.0 + 1.0; | |
vec4 sh = -step( h, vec4( 0.0 ) ); | |
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; | |
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; | |
vec3 p0 = vec3( a0.xy, h.x ); | |
vec3 p1 = vec3( a0.zw, h.y ); | |
vec3 p2 = vec3( a1.xy, h.z ); | |
vec3 p3 = vec3( a1.zw, h.w ); | |
// Normalise gradients | |
vec4 norm = taylorInvSqrt( vec4( dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3) ) ); | |
p0 *= norm.x; | |
p1 *= norm.y; | |
p2 *= norm.z; | |
p3 *= norm.w; | |
// Mix final noise value | |
vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3) ), 0.0 ); | |
m = m * m; | |
return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), | |
dot(p2,x2), dot(p3,x3) ) ); | |
} | |
float heightMap( vec3 coord ) { | |
float n = abs( snoise( coord ) ); | |
n += 0.25 * abs( snoise( coord * 2.0 ) ); | |
n += 0.25 * abs( snoise( coord * 4.0 ) ); | |
n += 0.125 * abs( snoise( coord * 8.0 ) ); | |
n += 0.0625 * abs( snoise( coord * 16.0 ) ); | |
return n; | |
} | |
void main( void ) { | |
// height | |
float n = heightMap( vTexCoord3D ); | |
// color | |
gl_FragColor = vec4( vec3( 1.5 - n, 1.0 - n, 0.5 - n ), 1.0 ); | |
// normal | |
const float e = 0.001; | |
float nx = heightMap( vTexCoord3D + vec3( e, 0.0, 0.0 ) ); | |
float ny = heightMap( vTexCoord3D + vec3( 0.0, e, 0.0 ) ); | |
float nz = heightMap( vTexCoord3D + vec3( 0.0, 0.0, e ) ); | |
vec3 normal = normalize( vNormal + 0.05 * vec3( n - nx, n - ny, n - nz ) / e ); | |
// diffuse light | |
vec3 vLightWeighting = vec3( 0.1 ); | |
vec4 lDirection = viewMatrix * vec4( normalize( vec3( 1.0, 0.0, 0.5 ) ), 0.0 ); | |
float directionalLightWeighting = dot( normal, normalize( lDirection.xyz ) ) * 0.25 + 0.75; | |
vLightWeighting += vec3( 1.0 ) * directionalLightWeighting; | |
// specular light | |
vec3 dirHalfVector = normalize( lDirection.xyz + normalize( vViewPosition ) ); | |
float dirDotNormalHalf = dot( normal, dirHalfVector ); | |
float dirSpecularWeight = 0.0; | |
if ( dirDotNormalHalf >= 0.0 ) | |
dirSpecularWeight = ( 1.0 - n ) * pow( dirDotNormalHalf, 5.0 ); | |
vLightWeighting += vec3( 1.0, 0.5, 0.0 ) * dirSpecularWeight * n * 2.0; | |
gl_FragColor *= vec4( vLightWeighting, 1.0 ); | |
} | |
script#vertexNoise(type='x-shader/x-vertex'). | |
uniform float time; | |
uniform float scale; | |
varying vec3 vTexCoord3D; | |
varying vec3 vNormal; | |
varying vec3 vViewPosition; | |
void main( void ) { | |
vec4 mPosition = modelMatrix * vec4( position, 1.0 ); | |
vNormal = normalize( normalMatrix * normal ); | |
vViewPosition = cameraPosition - mPosition.xyz; | |
vTexCoord3D = scale * ( position.xyz + vec3( 0.0, 0.0, -time ) ); | |
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | |
} | |
script#vertexGlow(type='x-shader/x-vertex'). | |
uniform vec3 viewVector; | |
uniform float c; | |
uniform float p; | |
varying float intensity; | |
void main( void ) { | |
vec3 vNormal = normalize( normalMatrix * normal ); | |
vec3 vNormel = normalize( normalMatrix * viewVector ); | |
intensity = pow( c - dot(vNormal, vNormel), p ); | |
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | |
} | |
script#fragmentGlow(type='x-shader/x-vertex'). | |
uniform vec3 glowColor; | |
varying float intensity; | |
void main( void ) { | |
vec3 glow = glowColor * intensity; | |
gl_FragColor = vec4( glow, 1.0 ); | |
} |
/** | |
* This script will only work on browsers with AudioContext and WebGL active, in short: get the latest Chrome. | |
* IE11 at the time of writing does not support AudioContext, but it renders WebGL pretty good. | |
* I know Safari (6) has WebGL issues and you might need to enable it on your settings/flags, also doesn't support AudioContext not sure of the latest. | |
* Don't even think to run this on mobile Safari. | |
* I haven't tested Firefox but unless you're running an ancient version I am pretty confident this works on FF too. | |
* | |
* Built with Mrdoob's Threejs (http://threejs.org), keyboard and probably LSD. | |
* Using Michael Bromley's code to grab music from Soundcloud and analyze it (https://github.com/michaelbromley/soundcloud-visualizer) | |
* | |
* Below the well commented dark magic. | |
* | |
* | |
* Allowed to fork, play with this, post it on your website, show to your mum as long you give credit to the original owners. | |
* | |
* Luigi Mannoni (http://luigimannoni.com) | |
* Twitter: @mashermack | |
* More experiments on: | |
* Codepen: http://codepen.io/luigimannoni | |
* Github: https://github.com/luigimannoni/luigimannoni.github.io | |
* Last update on 18/06/2015 - Fixed CrossOrigin bug | |
* Last update on 29/07/2015 - Added warning | |
*/ | |
/** | |
* Michael Bromley's Soundcloud Analyzer | |
* https://github.com/michaelbromley/soundcloud-visualizer) | |
*/ | |
var audioCtxCheck = window.AudioContext || window.webkitAudioContext; | |
if (!audioCtxCheck) { | |
document.getElementById('warning').style.display = 'block'; | |
document.getElementById('player').style.display = 'none'; | |
} | |
else { | |
var SoundCloudAudioSource = function(player) { | |
var self = this; | |
var analyser; | |
var audioCtx = new (window.AudioContext || window.webkitAudioContext); | |
analyser = audioCtx.createAnalyser(); | |
analyser.fftSize = 256; | |
var source = audioCtx.createMediaElementSource(player); | |
source.connect(analyser); | |
analyser.connect(audioCtx.destination); | |
var sampleAudioStream = function() { | |
analyser.getByteFrequencyData(self.streamData); | |
// Calculate an overall volume value | |
var total = 0; | |
for (var i = 0; i < 64; i++) { // Get the volume from the first 64 bins | |
total += self.streamData[i]; | |
} | |
self.volume = total; | |
var totalLow = 0; | |
for (var i = 0; i < 31; i++) { // Get the volume from the first 32 bins | |
totalLow += self.streamData[i]; | |
} | |
self.volumeLow = totalLow; | |
var totalHi = 0; | |
for (var i = 31; i < 64; i++) { // Get the volume from the second 32 bins | |
totalHi += self.streamData[i]; | |
} | |
self.volumeHi = totalHi; | |
}; | |
setInterval(sampleAudioStream, 20); | |
// Public properties and methods | |
this.volume = 0; | |
this.volumeLow = 0; | |
this.volumeHi = 0; | |
this.streamData = new Uint8Array(256); | |
this.playStream = function(streamUrl) { | |
// Get the input stream from the audio element | |
player.addEventListener('ended', function(){ | |
self.directStream('coasting'); | |
}); | |
player.crossOrigin = 'anonymous'; | |
player.setAttribute('src', streamUrl); | |
player.play(); | |
} | |
}; | |
var Visualizer = function() { | |
var audioSource; | |
this.init = function(options) { | |
audioSource = options.audioSource; | |
var container = document.getElementById(options.containerId); | |
}; | |
}; | |
var SoundcloudLoader = function(player,uiUpdater) { | |
var self = this; | |
var client_id = "26095b994cc185bc665f4c9fcce8f211"; // to get an ID go to http://developers.soundcloud.com/ | |
this.sound = {}; | |
this.streamUrl = ""; | |
this.errorMessage = ""; | |
this.player = player; | |
/** | |
* Loads the JSON stream data object from the URL of the track (as given in the location bar of the browser when browsing Soundcloud), | |
* and on success it calls the callback passed to it (for example, used to then send the stream_url to the audiosource object). | |
* @param track_url | |
* @param callback | |
*/ | |
this.loadStream = function(track_url, successCallback, errorCallback) { | |
SC.initialize({ | |
client_id: client_id | |
}); | |
SC.get('/resolve', { url: track_url }, function(sound) { | |
if (sound.errors) { | |
self.errorMessage = ""; | |
for (var i = 0; i < sound.errors.length; i++) { | |
self.errorMessage += sound.errors[i].error_message + '<br>'; | |
} | |
self.errorMessage += 'Make sure the URL has the correct format: https://soundcloud.com/user/title-of-the-track'; | |
errorCallback(); | |
} else { | |
if(sound.kind=="playlist"){ | |
self.sound = sound; | |
self.streamPlaylistIndex = 0; | |
self.streamUrl = function(){ | |
return sound.tracks[self.streamPlaylistIndex].stream_url + '?client_id=' + client_id; | |
} | |
successCallback(); | |
}else{ | |
self.sound = sound; | |
self.streamUrl = function(){ return sound.stream_url + '?client_id=' + client_id; }; | |
successCallback(); | |
} | |
} | |
}); | |
}; | |
this.directStream = function(direction){ | |
if(direction=='toggle'){ | |
if (this.player.paused) { | |
this.player.play(); | |
} else { | |
this.player.pause(); | |
} | |
} | |
else if(this.sound.kind=="playlist"){ | |
if(direction=='coasting') { | |
this.streamPlaylistIndex++; | |
}else if(direction=='forward') { | |
if(this.streamPlaylistIndex>=this.sound.track_count-1) this.streamPlaylistIndex = 0; | |
else this.streamPlaylistIndex++; | |
}else{ | |
if(this.streamPlaylistIndex<=0) this.streamPlaylistIndex = this.sound.track_count-1; | |
else this.streamPlaylistIndex--; | |
} | |
if(this.streamPlaylistIndex>=0 && this.streamPlaylistIndex<=this.sound.track_count-1) { | |
this.player.setAttribute('src',this.streamUrl()); | |
this.player.play(); | |
} | |
} | |
} | |
}; | |
var visualizer = new Visualizer(); | |
var player = document.getElementById('player'); | |
var loader = new SoundcloudLoader(player); | |
var audioSource = new SoundCloudAudioSource(player); | |
var form = document.getElementById('form'); | |
var loadAndUpdate = function(trackUrl) { | |
loader.loadStream(trackUrl, | |
function() { | |
audioSource.playStream(loader.streamUrl()); | |
}, function(){}); | |
}; | |
visualizer.init({ | |
containerId: 'visualizer', | |
audioSource: audioSource | |
}); | |
// On load, check to see if there is a track token in the URL, and if so, load that automatically | |
if (window.location.hash) { | |
var trackUrl = 'https://soundcloud.com/' + window.location.hash.substr(1); | |
loadAndUpdate(trackUrl); | |
} | |
else { | |
var trackUrl = 'https://soundcloud.com/' + 'mhd-underground/mehdispoz-space-travel-unrelease'; | |
loadAndUpdate(trackUrl); | |
} | |
} | |
// Since I suck at trigonometry I'll just convert radii into degrees. | |
function deg2rad(_degrees) { | |
return (_degrees * Math.PI / 180); | |
} | |
/** | |
* WebGL Logic | |
*/ | |
var controls; | |
var scene = new THREE.Scene(); | |
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 10000); | |
var innerColor = 0xff0000, | |
outerColor = 0xff9900; | |
var innerSize = 32, | |
outerSize = 64; | |
var renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setClearColor( 0x000000, 0 ); // background | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
document.body.appendChild(renderer.domElement); | |
controls = new THREE.TrackballControls( camera ); | |
controls.noPan = true; | |
controls.minDistance = 120; | |
controls.maxDistance = 650; | |
// | |
var imageLoader = new THREE.TextureLoader(); | |
imageLoader.setCrossOrigin('anonymous'); | |
// Mesh | |
var group = new THREE.Group(); | |
scene.add(group); | |
// Lights | |
var light = new THREE.AmbientLight( 0x404040 ); // soft white light | |
scene.add( light ); | |
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 ); | |
directionalLight.position.set( 0, 128, 128 ); | |
scene.add( directionalLight ); | |
// Skybox | |
var geometry = new THREE.BoxGeometry( 5000, 5000, 5000 ); | |
var materialArray = []; | |
var directions = ['right1', 'left2', 'top3', 'bottom4', 'front5', 'back6']; | |
for (var i = 0; i < 6; i++) { | |
materialArray.push( new THREE.MeshBasicMaterial({ | |
map: imageLoader.load( '//luigimannoni.github.io/experiments/spacesphere-webgl/javascripts/bluenebula1024_' + directions[i] + '.png' ), | |
side: THREE.BackSide | |
})); | |
} | |
var material = new THREE.MeshFaceMaterial( materialArray ); | |
var skybox = new THREE.Mesh( geometry, material ); | |
scene.add( skybox ); | |
// Sun | |
var uniforms = { | |
time: { type: "f", value: 1.0 }, | |
scale: { type: "f", value: 0.05 } | |
}; | |
var star = new THREE.Mesh( | |
new THREE.SphereGeometry( innerSize, 32, 32 ), | |
new THREE.ShaderMaterial( { | |
uniforms: uniforms, | |
vertexShader: document.getElementById( 'vertexNoise' ).textContent, | |
fragmentShader: document.getElementById( 'fragmentNoise' ).textContent | |
}) | |
); | |
scene.add(star); | |
var glow = new THREE.Mesh( | |
new THREE.SphereGeometry( innerSize+2, 32, 32 ), | |
new THREE.ShaderMaterial( { | |
uniforms: { | |
"c": { type: "f", value: 1 }, | |
"p": { type: "f", value: 6 }, | |
glowColor: { type: "c", value: new THREE.Color(0xff3300) }, | |
viewVector: { type: "v3", value: camera.position } | |
}, | |
vertexShader: document.getElementById( 'vertexGlow' ).textContent, | |
fragmentShader: document.getElementById( 'fragmentGlow' ).textContent, | |
side: THREE.BackSide, | |
blending: THREE.AdditiveBlending, | |
transparent: true, | |
alphaTest: 0.2, | |
}) | |
); | |
scene.add(glow); | |
var sphereOuter = new THREE.Group(); | |
var icosahedronGeometry = new THREE.IcosahedronGeometry( outerSize, 3 ); | |
// materials | |
var materials = [], | |
matCopy = []; | |
for (var i = 0; i < 89; i++) { | |
materials.push(new THREE.MeshPhongMaterial( { color: innerColor, shading: THREE.FlatShading, side: THREE.DoubleSide, transparent: true, opacity: 0.8 } )); | |
matCopy.push(new THREE.MeshPhongMaterial( { color: innerColor, shading: THREE.FlatShading, side: THREE.DoubleSide, transparent: true, opacity: 0.8 } )); | |
}; | |
// assign material to each face | |
for( var i = 0; i < icosahedronGeometry.faces.length; i++ ) { | |
icosahedronGeometry.faces[ i ].materialIndex = THREE.Math.randInt(0, materials.length-1 ); | |
} | |
icosahedronGeometry.sortFacesByMaterialIndex(); // optional, to reduce draw calls | |
// Sphere Wireframe Outer | |
var icosahedronOuter = new THREE.Mesh( | |
icosahedronGeometry, | |
new THREE.MeshFaceMaterial(materials) | |
); | |
/*var icosahedronOuter = new THREE.Mesh( | |
icosahedronGeometry, | |
new THREE.MeshLambertMaterial({ | |
color: outerColor, | |
ambient: outerColor, | |
wireframe: false, | |
transparent: true, | |
shading: THREE.FlatShading, | |
opacity: 1, | |
//alphaMap: imageLoader.load( '//luigimannoni.github.io/experiments/spacesphere-webgl/javascripts/alphamap.jpg' ), | |
shininess: 0 | |
}) | |
);*/ | |
sphereOuter.add(icosahedronOuter); | |
// The icosahedron helper | |
var icoEdgeHelper = new THREE.EdgesHelper( icosahedronOuter, innerColor ); | |
icoEdgeHelper.material.linewidth = 0.5; | |
icoEdgeHelper.material.opacity = 0.2; | |
icoEdgeHelper.material.transparent = true; | |
scene.add( icoEdgeHelper ); // Needs to stay outside | |
// Clone geometry | |
var icoGeometryBuffer = new THREE.BufferGeometry(); | |
icoGeometryBuffer.fromGeometry(icosahedronOuter.geometry); | |
// Particles Inner | |
var particlesInner = new THREE.Points(icoGeometryBuffer, new THREE.PointsMaterial({ | |
size: 2, | |
color: innerColor, | |
map: imageLoader.load( '//luigimannoni.github.io/experiments/spacesphere-webgl/javascripts/particletexture.png' ), | |
transparent: true, | |
}) | |
); | |
sphereOuter.add(particlesInner); | |
scene.add(sphereOuter); | |
// Starfield | |
var geometry = new THREE.Geometry(); | |
for (i = 0; i < 5000; i++) { | |
var vertex = new THREE.Vector3(); | |
vertex.x = Math.random()*2000-1000; | |
vertex.y = Math.random()*2000-1000; | |
vertex.z = Math.random()*2000-1000; | |
geometry.vertices.push(vertex); | |
} | |
var starField = new THREE.Points(geometry, new THREE.PointsMaterial({ | |
size: 10, | |
color: 0xffffff, | |
map: imageLoader.load( '//luigimannoni.github.io/experiments/spacesphere-webgl/javascripts/particletextureshaded.png' ), | |
transparent: true | |
}) | |
); | |
scene.add(starField); | |
camera.position.z = -110; | |
var time = new THREE.Clock(); | |
// var bS, rS, glS, tS; | |
// bS = new BrowserStats(); | |
// glS = new glStats(); | |
// tS = new threeStats( renderer ); | |
// rS = new rStats( { | |
// CSSPath: '../../libs/rstats/', | |
// values: { | |
// frame: { caption: 'Total frame time (ms)', over: 16, average: true, avgMs: 100 }, | |
// fps: { caption: 'Framerate (FPS)', below: 30 }, | |
// calls: { caption: 'Calls (three.js)', over: 3000 }, | |
// raf: { caption: 'Time since last rAF (ms)', average: true, avgMs: 100 }, | |
// rstats: { caption: 'rStats update (ms)', average: true, avgMs: 100 }, | |
// texture: { caption: 'GenTex', average: true, avgMs: 100 }, | |
// mental: { caption: 'Mental Var', over: 0.8 }, | |
// volumeLow: { caption: 'Volume Low', over: 4000 }, | |
// volumeHigh: { caption: 'Volume High', over: 4000 }, | |
// }, | |
// groups: [ | |
// { caption: 'Framerate', values: [ 'fps', 'raf' ] }, | |
// { caption: 'Frame Budget', values: [ 'frame', 'texture', 'setup', 'render' ] } | |
// ], | |
// fractions: [ | |
// { base: 'frame', steps: [ 'texture', 'setup', 'render' ] } | |
// ], | |
// plugins: [ | |
// bS, | |
// tS, | |
// glS | |
// ] | |
// } ); | |
var render = function () { | |
// rS( 'frame' ).start(); | |
// glS.start(); | |
// | |
// rS( 'rAF' ).tick(); | |
// rS( 'FPS' ).frame(); | |
// rS( 'setup' ).start(); | |
// rS( 'setup' ).end(); | |
// | |
// rS( 'render' ).start(); | |
renderer.render(scene, camera); | |
var innerShift = Math.abs(Math.cos(( (time.getElapsedTime()+2.5) / 20))) / 10; | |
var outerShift = Math.abs(Math.cos(( (time.getElapsedTime()+5) / 10))); | |
var superShift = Math.abs(Math.cos(( (time.getElapsedTime()+5) / 20))); | |
if (audioCtxCheck && player.paused == false) { | |
// Audio Context is supported | |
var mental = (Math.min(Math.max((Math.tan(audioSource.volumeHi/6500) * 0.5)), 2)); | |
var volume = { | |
low: audioSource.volumeLo, | |
high: audioSource.volumeHi, | |
}; | |
// rS( 'mental' ).set(mental); | |
// rS( 'volumeLow' ).set(volume.low); | |
// rS( 'volumeHigh' ).set(volume.high); | |
uniforms.time.value += 0.02 + (mental*0.1); | |
sphereOuter.scale.set(1+(mental*0.3), 1+(mental*0.3), 1+(mental*0.3)); | |
icoEdgeHelper.material.color.setHSL(superShift, 1, mental); | |
particlesInner.material.color.setHSL(superShift, 1, mental); | |
// rS( 'innerShift' ).set(innerShift); | |
// rS( 'outerShift' ).set(outerShift); | |
for (var i = 0; i < materials.length; i++) { | |
materials[i].color.setHSL(superShift, 1 / 255 * audioSource.streamData[i], 0.5); | |
materials[i].opacity = 1 / 255 * audioSource.streamData[i]; | |
} | |
// rS( 'shuffling' ).start(); | |
if (mental > 0.7) { | |
for( var i = 0; i < materials.length; i++ ) { | |
var applyThis = THREE.Math.randInt(0, materials.length-1); | |
icosahedronOuter.material.materials[i] = matCopy[applyThis]; | |
} | |
} | |
// rS( 'shuffling' ).end(); | |
} | |
else { | |
// Fallback for no support for AudioContext | |
uniforms.time.value += 0.02; | |
for (var i = 0; i < materials.length; i++) { | |
materials[i].color.setHSL(innerShift, 1, 0.5); | |
materials[i].opacity = outerShift; | |
} | |
icoEdgeHelper.material.color.setHSL(innerShift, 1, 0.5); | |
particlesInner.material.color.setHSL(innerShift, 1, 0.5); | |
} | |
directionalLight.position.x = Math.cos(time.getElapsedTime()/0.5)*128; | |
directionalLight.position.y = Math.cos(time.getElapsedTime()/0.5)*128; | |
directionalLight.position.z = Math.sin(time.getElapsedTime()/0.5)*128; | |
skybox.rotation.y -= 0.0005; | |
starField.rotation.y -= 0.002; | |
sphereOuter.rotation.x += 0.001; | |
sphereOuter.rotation.z += 0.001; | |
// rS( 'render' ).end(); | |
// rS( 'frame' ).end(); | |
controls.update(); | |
// rS( 'rStats' ).start(); | |
// rS().update(); | |
// rS( 'rStats' ).end(); | |
requestAnimationFrame(render); | |
}; | |
render(); // Ciao | |
// Mouse and resize events | |
window.addEventListener('resize', onWindowResize, false); | |
function onWindowResize() { | |
camera.aspect = window.innerWidth / window.innerHeight; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(window.innerWidth, window.innerHeight); | |
} |
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r73/three.min.js"></script> | |
<script src="//luigimannoni.github.io/libs/threejs/controls/TrackballControls.js"></script> | |
<script src="//connect.soundcloud.com/sdk.js"></script> |
@import "compass/css3"; | |
@import "compass/reset"; | |
html,body {height: 100%;} | |
body {margin: 0;background:#000 url(http://i.imgur.com/p7LPJ6M.png) no-repeat center; background-size:cover} | |
canvas { | |
position: absolute; | |
top: 0;left: 0;bottom: 0;right: 0; | |
cursor: -webkit-grab; | |
cursor:-moz-grab; | |
&:active { | |
cursor: -webkit-grabbing; | |
cursor:-moz-grabbing; | |
} | |
} | |
canvas {position: absolute; top: 0;left: 0;bottom: 0;right: 0;} | |
audio {position: fixed; left: 10px;bottom: 10px;right: 10px;z-index:10000; width: 100%;} | |
.collection { | |
position: fixed; | |
top: 0; | |
width: 100%; | |
z-index: 10000; | |
a { | |
display: block; | |
padding: 5px; | |
background: rgba(0,0,0,0.6); | |
color:#fff; | |
text-decoration: none; | |
font: 700 12px Consolas, system, monospace; | |
@include transition(all 250ms linear); | |
&:hover { | |
background: #fff; | |
color: #000; | |
} | |
} | |
.prev {text-align: left;float: left;} | |
.next {text-align: right;float: right;} | |
} | |
#warning { | |
font: 700 12px Consolas, system, monospace; | |
color: #fff; | |
background: #c82222; | |
position: absolute; | |
bottom: 45%; | |
left: 0; | |
right: 0; | |
z-index: 10001; | |
text-align: center; | |
padding: 20px; | |
display: none; | |
} |
ThreeJS/WebGL Soundcloud player/visualizer based on HTML5 AudioContext API, see JS comments for full list of credits. PS: Doesn't load on the preview grid
A Pen by Luigi Mannoni on CodePen.