<!-- Inspired by @paulofalcao's experiment http://glsl.heroku.com/e#4766.0 --><div id="container">
<div id="nogl">
<h1>Aww, No WebGL :(</h1>
<p>This experiment requires WebGL to run. Please enable it, or come back and visit with a <a href="http://caniuse.com/webgl" target="_blank">compatible browser</a>.</p>
</div>
</div>
<header class="info">
<hgroup class="about">
<h1>Plasmatic Isosurface</h1>
<h2>WebGL / GLSL plasma simulation running on the GPU</h2>
<h3>Requires <a href="http://caniuse.com/webgl" target="_blank">WebGL</a></h3>
</hgroup>
<nav class="more">
<a href="https://github.com/soulwire/Plasmatic-Isosurface/archive/master.zip" target="_blank">Download</a>
<a href="https://github.com/soulwire/Plasmatic-Isosurface" target="_blank">View on Github</a>
</nav>
</header>
html, body {
font-family: 'Quantico', sans-serif;
background: #111;
overflow: hidden;
margin: 0;
}
#nogl {
box-sizing: border-box;
box-sizing: border-box;
box-sizing: border-box;
box-sizing: border-box;
box-sizing: border-box;
border-radius: 3px;
line-height: 1.45;
text-align: justify;
box-shadow: 0px 0px 0px 4px rgba(255,0,41,0.2);
background: #FF0029;
font-size: 13px;
position: absolute;
padding: 10px 20px;
display: none;
margin: -70px 0 0 -130px;
height: 140px;
width: 260px;
color: #fff;
left: 50%;
top: 50%;
}
#nogl h1 {
font-weight: 300;
font-size: 18px;
}
#nogl p {
color: rgba(255,255,255,0.7);
}
#nogl a {
color: #fff;
}
/* Info */
@-webkit-keyframes show-info {
0% { transform: rotateY(120deg); }
100% { transform: rotateY(0deg); }
}
@-moz-keyframes show-info {
0% { transform: rotateY(120deg); }
100% { transform: rotateY(0deg); }
}
@-ms-keyframes show-info {
0% { transform: rotateY(120deg); }
100% { transform: rotateY(0deg); }
}
@-o-keyframes show-info {
0% { transform: rotateY(120deg); }
100% { transform: rotateY(0deg); }
}
@keyframes show-info {
0% { transform: rotateY(120deg); }
100% { transform: rotateY(0deg); }
}
.info {
transition: all 180ms ease-out;
transition: all 180ms ease-out;
transition: all 180ms ease-out;
transition: all 180ms ease-out;
transition: all 180ms ease-out;
transform-style: preserve-3d;
transform-style: preserve-3d;
transform-style: preserve-3d;
transform-style: preserve-3d;
transform-style: preserve-3d;
transform: perspective(800);
transform: perspective(800);
transform: perspective(800);
transform: perspective(800);
transform: perspective(800);
position: absolute;
font-size: 12px;
opacity: 0.8;
color: #fff;
width: 240px;
left: 0px;
top: 20px;
}
.info:hover {
box-shadow: 0 0 0 4px rgba(255,255,255,0.05);
opacity: 1.0;
}
.info h1,
.info h2,
.info h3 {
line-height: 1;
margin: 5px 0;
}
.info a {
transition: all 200ms ease-out;
transition: all 200ms ease-out;
transition: all 200ms ease-out;
transition: all 200ms ease-out;
transition: all 200ms ease-out;
border-bottom: 1px dotted rgba(255,255,255,0.4);
text-decoration: none;
opacity: 0.6;
color: #fff;
}
.info a:hover {
opacity: 0.99;
}
.info .about, .info .more {
transform-origin: 0% 50%;
transform-origin: 0% 50%;
transform-origin: 0% 50%;
transform-origin: 0% 50%;
transform-origin: 0% 50%;
transform: rotateY(120deg);
transform: rotateY(120deg);
transform: rotateY(120deg);
transform: rotateY(120deg);
transform: rotateY(120deg);
margin-bottom: 1px;
background: rgba(0,0,0,0.8);
padding: 12px 15px 12px 20px;
}
.info .about {
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 600ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 600ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 600ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 600ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 600ms 1 normal forwards;
padding-bottom: 15px;
}
.info .about a {
opacity: 0.9;
}
.info .about h1 {
letter-spacing: -1px;
font-weight: 300;
font-size: 19px;
opacity: 0.95;
}
.info .about h2 {
font-weight: 300;
font-size: 13px;
opacity: 0.8;
}
.info .about h3 {
text-transform: uppercase;
margin-top: 10px;
font-size: 11px;
}
.info .about h3:before {
margin-right: 2px;
font-size: 14px;
content: '\203A';
}
.info .more {
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 500ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 500ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 500ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 500ms 1 normal forwards;
animation: show-info 500ms cubic-bezier(0.230, 1.000, 0.320, 1.000) 500ms 1 normal forwards;
padding: 5px 15px 10px 20px;
}
.info .more a {
text-transform: uppercase;
margin-right: 10px;
font-size: 10px;
}
GLSL =
# Vertex shader
vert: """
#ifdef GL_ES
precision mediump float;
#endif
// Uniforms
uniform vec2 u_resolution;
// Attributes
attribute vec2 a_position;
void main() {
gl_Position = vec4 (a_position, 0, 1);
}
"""
# Fragment shader
frag: """
#ifdef GL_ES
precision mediump float;
#endif
uniform bool u_scanlines;
uniform vec2 u_resolution;
uniform float u_brightness;
uniform float u_blobiness;
uniform float u_particles;
uniform float u_millis;
uniform float u_energy;
// https://goo.gl/LrCde
float noise( vec2 co ){
return fract( sin( dot( co.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453 );
}
void main( void ) {
vec2 position = ( gl_FragCoord.xy / u_resolution.x );
float t = u_millis * 0.001 * u_energy;
float a = 0.0;
float b = 0.0;
float c = 0.0;
vec2 pos, center = vec2( 0.5, 0.5 * (u_resolution.y / u_resolution.x) );
float na, nb, nc, nd, d;
float limit = u_particles / 40.0;
float step = 1.0 / u_particles;
float n = 0.0;
for ( float i = 0.0; i <= 1.0; i += 0.025 ) {
if ( i <= limit ) {
vec2 np = vec2(n, 1-1);
na = noise( np * 1.1 );
nb = noise( np * 2.8 );
nc = noise( np * 0.7 );
nd = noise( np * 3.2 );
pos = center;
pos.x += sin(t*na) * cos(t*nb) * tan(t*na*0.15) * 0.3;
pos.y += tan(t*nc) * sin(t*nd) * 0.1;
d = pow( 1.6*na / length( pos - position ), u_blobiness );
if ( i < limit * 0.3333 ) a += d;
else if ( i < limit * 0.6666 ) b += d;
else c += d;
n += step;
}
}
vec3 col = vec3(a*c,b*c,a*b) * 0.0001 * u_brightness;
if ( u_scanlines ) {
col -= mod( gl_FragCoord.y, 2.0 ) < 1.0 ? 0.5 : 0.0;
}
gl_FragColor = vec4( col, 1.0 );
}
"""
try
gl = Sketch.create
# Sketch settings
container: document.getElementById 'container'
type: Sketch.WEB_GL
# Uniforms
brightness: 0.8
blobiness: 1.5
particles: 40
energy: 1.01
scanlines: yes
catch error
nogl = document.getElementById 'nogl'
nogl.style.display = 'block'
if gl
gl.setup = ->
this.clearColor 0.0, 0.0, 0.0, 1.0
# Setup shaders
vert = @createShader @VERTEX_SHADER
frag = @createShader @FRAGMENT_SHADER
@shaderSource vert, GLSL.vert
@shaderSource frag, GLSL.frag
@compileShader vert
@compileShader frag
throw @getShaderInfoLog vert if not @getShaderParameter vert, @COMPILE_STATUS
throw @getShaderInfoLog frag if not @getShaderParameter frag, @COMPILE_STATUS
@shaderProgram = do @createProgram
@.attachShader @shaderProgram, vert
@.attachShader @shaderProgram, frag
@linkProgram @shaderProgram
throw 'Failed to initialise shaders' if not @getProgramParameter @shaderProgram, @LINK_STATUS
@useProgram @shaderProgram
# Store attribute / uniform locations
@shaderProgram.attributes =
position: @getAttribLocation @shaderProgram, 'a_position'
@shaderProgram.uniforms =
resolution: @getUniformLocation @shaderProgram, 'u_resolution'
brightness: @getUniformLocation @shaderProgram, 'u_brightness'
blobiness: @getUniformLocation @shaderProgram, 'u_blobiness'
particles: @getUniformLocation @shaderProgram, 'u_particles'
scanlines: @getUniformLocation @shaderProgram, 'u_scanlines'
energy: @getUniformLocation @shaderProgram, 'u_energy'
millis: @getUniformLocation @shaderProgram, 'u_millis'
# Create geometry
@geometry = do @createBuffer
@geometry.vertexCount = 6
@bindBuffer @ARRAY_BUFFER, @geometry
@bufferData @ARRAY_BUFFER, new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0]),
@STATIC_DRAW
@enableVertexAttribArray @shaderProgram.attributes.position
@vertexAttribPointer @shaderProgram.attributes.position, 2, @FLOAT, no, 0, 0
# Resize to window
do @resize
gl.updateUniforms = ->
return if not @shaderProgram
@uniform2f @shaderProgram.uniforms.resolution, @width, @height
@uniform1f @shaderProgram.uniforms.brightness, @brightness
@uniform1f @shaderProgram.uniforms.blobiness, @blobiness
@uniform1f @shaderProgram.uniforms.particles, @particles
@uniform1i @shaderProgram.uniforms.scanlines, @scanlines
@uniform1f @shaderProgram.uniforms.energy, @energy
gl.draw = ->
# Update uniforms
@uniform1f @shaderProgram.uniforms.millis, @millis + 5000
# Render
@clear @COLOR_BUFFER_BIT | @DEPTH_BUFFER_BIT
@bindBuffer @ARRAY_BUFFER, @geometry
@drawArrays @TRIANGLES, 0, @geometry.vertexCount
gl.resize = ->
# Update resolution
@viewport 0, 0, @width, @height
# Update uniforms if the shader program is ready
do @updateUniforms
# GUI
gui = new dat.GUI()
gui.add( gl, 'particles' ).step( 1.0 ).min( 8 ).max( 40 ).onChange -> do gl.updateUniforms
gui.add( gl, 'brightness' ).step( 0.01 ).min( 0.1 ).max( 1.0 ).onChange -> do gl.updateUniforms
gui.add( gl, 'blobiness' ).step( 0.01 ).min( 0.8 ).max( 1.5 ).onChange -> do gl.updateUniforms
gui.add( gl, 'energy' ).step( 0.01 ).min( 0.1 ).max( 4.0 ).onChange -> do gl.updateUniforms
gui.add( gl, 'scanlines' ).onChange -> do gl.updateUniforms
gui.close()
View Compiled
This Pen doesn't use any external CSS resources.