Skip to content

Instantly share code, notes, and snippets.

@Frankie-666
Created May 7, 2016 20:54
Show Gist options
  • Save Frankie-666/f9d65c564a37c86629d330a3227621cb to your computer and use it in GitHub Desktop.
Save Frankie-666/f9d65c564a37c86629d330a3227621cb to your computer and use it in GitHub Desktop.
WebGL Spacesphere
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;
}

WebGL Spacesphere

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.

License.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment