cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

Quick-add: + add another resource

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.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

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.

            
              <body>
  <canvas id="canvas" width="1400" height="600"></canvas>
</body>
            
          
!
            
              body {
  background-color: #000;    
  margin: 0;
  overflow: hidden;
  background-repeat: no-repeat;
}
            
          
!
            
              //2D raphics using WebGL and GLSL
//Based on:
//http://jamie-wong.com/2016/07/06/metaballs-and-webgl/
//https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial
//http://learningwebgl.com/blog/?p=1786
//https://dev.opera.com/articles/webgl-post-processing/
//https://webglfundamentals.org/
//And many more

const mobile = ( navigator.userAgent.match(/Android/i)
    || navigator.userAgent.match(/webOS/i)
    || navigator.userAgent.match(/iPhone/i)
    //|| navigator.userAgent.match(/iPad/i)
    || navigator.userAgent.match(/iPod/i)
    || navigator.userAgent.match(/BlackBerry/i)
    || navigator.userAgent.match(/Windows Phone/i)
);

var canvas = document.getElementById("canvas");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var WIDTH = Math.floor(canvas.width/2.0);
var HEIGHT = canvas.height;

// Initialize the GL context
var gl = canvas.getContext('webgl');

if(!gl){
  alert("Unable to initialize WebGL.");
}
//Time step
var dt = 0.03;
//Time
var time = 0.0;
var bloom = 20;
var blurFactor = 4;
var blurCount = 5;
//For colour modes
var type = 0;
var toggle_render = true;

//-----------GUI-----------//
//dat.gui library controls
var toggle_render_button = { toggle_render:function(){
  toggle_render = !toggle_render;
}};
var red_button = { red:function(){
  type = 0;
}};
var green_button = { green:function(){
  type = 1;
}};
var blue_button = { blue:function(){
  type = 2;
}};
var gradient_button = { gradient:function(){
  type = 3;
}};


var gui = new dat.GUI();
gui.add(toggle_render_button, 'toggle_render');
gui.add(this, 'dt').min(0.0).max(0.05).step(0.005).listen();
gui.add(this, 'bloom').min(0.0).max(100.0).step(10).listen();
gui.add(this, 'blurFactor').min(0.0).max(6.0).step(1).listen();
gui.add(red_button, 'red');
gui.add(green_button, 'green');
gui.add(blue_button, 'blue');
gui.add(gradient_button, 'gradient');
gui.close();

function setSize(){
  material.size = size;
}

function setColour(){
  material.color.setHex(colour);
}


//GLSL code is presented as a string that is passed for compilation. 
//Can use quotes (' or "), which require escaping newline, or backtick (`), which doesn't.

//****************** Flame shaders ******************
//Specify vertex shader. (x,y) coordinates are variable, z is 0
var flameVertexSource = `
attribute vec2 position;
void main() {
  gl_Position = vec4(position, 0.0, 1.0);
}
`;

//Specify fragment shader. Set colour
var flameFragmentSource = `
precision highp float;
const float WIDTH = ` + WIDTH + `.0;
const float HEIGHT = ` + HEIGHT + `.0;
uniform float time;
uniform int type;

/*** WEBGL-NOISE FROM https://github.com/stegu/webgl-noise ***/

// Description : Array and textureless GLSL 2D simplex noise function.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : stegu
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//               https://github.com/stegu/webgl-noise
// 

vec3 mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec2 mod289(vec2 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x) {
  return mod289(((x*34.0)+1.0)*x);
}

float snoise(vec2 v)
{
  const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0
      0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
      -0.577350269189626,  // -1.0 + 2.0 * C.x
      0.024390243902439); // 1.0 / 41.0
  // First corner
  vec2 i  = floor(v + dot(v, C.yy) );
  vec2 x0 = v -   i + dot(i, C.xx);

  // Other corners
  vec2 i1;
  //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
  //i1.y = 1.0 - i1.x;
  i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  // x0 = x0 - 0.0 + 0.0 * C.xx ;
  // x1 = x0 - i1 + 1.0 * C.xx ;
  // x2 = x0 - 1.0 + 2.0 * C.xx ;
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;

  // Permutations
  i = mod289(i); // Avoid truncation effects in permutation
  vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
      + i.x + vec3(0.0, i1.x, 1.0 ));

  vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m ;
  m = m*m ;

  // Gradients: 41 points uniformly over a line, mapped onto a diamond.
  // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;

  // Normalise gradients implicitly by scaling m
  // Approximation of: m *= inversesqrt( a0*a0 + h*h );
  m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );

  // Compute final noise value at P
  vec3 g;
  g.x  = a0.x  * x0.x  + h.x  * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

/*** END WEBGL-NOISE ***/

void main() {
  float x = gl_FragCoord.x/(WIDTH);
  float y = gl_FragCoord.y/(WIDTH);
  float gradient = gl_FragCoord.y/(HEIGHT);

  //RGB values for flame
  float r = 1.0;
  float g = 0.0;
  float b = 0.0;

  //Get noise value at location with some mixing
  float noise = snoise(vec2(x/10.0,y/10.0 + 11.0));
  noise += gradient * snoise(vec2(x*2.0,y*2.0 + 1.5 * time));
  noise += gradient * snoise(vec2(x*3.0,y*3.0 + 2.0 * time));
  noise += gradient * snoise(vec2(x*6.0,y*6.0 + 3.0 * time));

  noise = max(0.0, noise);

  //Set colour mode
  if(type == 0){
    //Red to yellow
    g = 3.0 * noise * (gradient);
    b = noise * (gradient)/2.0; 
  }else if(type == 1){
    //Green
    r = noise * (gradient);
    g = 1.0;
    b = noise * (gradient)/2.0; 
  }else if(type == 2){
    //Blue
    r = noise * (gradient)/2.0; 
    g = 3.0 * noise * (gradient);
    b = 1.0; 
  }else if(type == 3){
    //Blue to magenta to yellow
    r = 3.0 * noise * (gradient * 10.0);
    g = noise * (gradient);
    b = 0.5 - gradient; 
  }

  noise *= 0.65*(1.0-gradient);

  //m = 1.0 if (gradient * 0.5) < noise, 0.0 otherwise.
  float m = step(gradient * 0.5, noise);

  gl_FragColor = vec4(m * r, m * g, m * b, 1.0);
}
`;

//****************** Blur shaders ******************

//Apply Gaussian blur with stencil size 11
//Based on a tutorial by ThinMatrix
var blurXVertexSource = `
attribute vec2 texPosition;
varying vec2 blurTextureCoords[11];
uniform float width;

void main() {
  //texCoord = texPosition;
  gl_Position = vec4(texPosition, 0.0, 1.0);
  vec2 centreTexCoords = texPosition * 0.5 + 0.5;
  float pixelSize = 1.0/width;

  for(int i = -5; i <= 5; i++){
    blurTextureCoords[i+5] = centreTexCoords + vec2(pixelSize * float(i), 0.0);
  }
}
`;

var blurYVertexSource = `
attribute vec2 texPosition;
varying vec2 blurTextureCoords[11];
uniform float height;

void main() {
  gl_Position = vec4(texPosition, 0.0, 1.0);
  vec2 centreTexCoords = texPosition * 0.5 + 0.5;
  float pixelSize = 1.0/height;

  for(int i = -5; i <= 5; i++){
    blurTextureCoords[i+5] = centreTexCoords + vec2(0.0, pixelSize * float(i));
  }
}
`;

var blurFragmentSource = `
precision highp float;

varying vec2 texCoord;
uniform sampler2D texData;

varying vec2 blurTextureCoords[11];

void main() {
  vec4 colour = vec4(0.0);
  colour += texture2D(texData, blurTextureCoords[0]) * 0.0093;
  colour += texture2D(texData, blurTextureCoords[1]) * 0.028002;
  colour += texture2D(texData, blurTextureCoords[2]) * 0.065984;
  colour += texture2D(texData, blurTextureCoords[3]) * 0.121703;
  colour += texture2D(texData, blurTextureCoords[4]) * 0.175713;
  colour += texture2D(texData, blurTextureCoords[5]) * 0.198596;
  colour += texture2D(texData, blurTextureCoords[6]) * 0.175713;
  colour += texture2D(texData, blurTextureCoords[7]) * 0.121703;
  colour += texture2D(texData, blurTextureCoords[8]) * 0.065984;
  colour += texture2D(texData, blurTextureCoords[9]) * 0.028002;
  colour += texture2D(texData, blurTextureCoords[10]) * 0.0093;
  gl_FragColor = colour;
}
`;


//****************** Bright shaders ******************

//Select only bright pixels in the domain
//Based on a tutorial by ThinMatrix
var brightVertexSource = `
attribute vec2 texPosition;
varying vec2 texCoord;

void main() {
  // map texture coordinates [0, 1] to world coordinates [-1, 1]
  // convert from 0->1 to 0->2
  // convert from 0->2 to -1->+1 (clipspace)

  texCoord = texPosition;
  gl_Position = vec4(texPosition*2.0-1.0, 0.0, 1.0);
}
`;

var brightFragmentSource = `
precision highp float;

varying vec2 texCoord;
uniform sampler2D brightData;
uniform float bloom;

void main() {
  vec4 colour = texture2D(brightData, texCoord);
  float brightness = (colour.r * 0.2126) + (colour.g * 0.7152) + (colour.b * 0.0722);
  gl_FragColor = colour * (brightness * bloom);
}
`;

//****************** Combine shaders ******************

var combineVertexSource = `
attribute vec2 texPosition;
varying vec2 texCoord;

void main() {
  // map texture coordinates [0, 1] to world coordinates [-1, 1]
  // convert from 0->1 to 0->2
  // convert from 0->2 to -1->+1 (clipspace)

  texCoord = texPosition;
  gl_Position = vec4(texPosition*2.0-1.0, 0.0, 1.0);
}
`;

var combineFragmentSource = `
precision highp float;

varying vec2 texCoord;
uniform sampler2D srcData;
uniform sampler2D blurData;

void main() {
  vec4 srcColour = texture2D(srcData, texCoord);
  vec4 blurColour = texture2D(blurData, texCoord);
  gl_FragColor = srcColour + blurColour;
}
`;

//****************** Utility functions ******************

//Compile shader and combine with source
function compileShader(shaderSource, shaderType){

  var shader = gl.createShader(shaderType);
  gl.shaderSource(shader, shaderSource);
  gl.compileShader(shader);

  if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
    throw "Shader compile failed with: " + gl.getShaderInfoLog(shader);
  }

  return shader;
}

//From https://codepen.io/jlfwong/pen/GqmroZ
// Utility to complain loudly if we fail to find the attribute
function getAttribLocation(program, name) {
  var attributeLocation = gl.getAttribLocation(program, name);
  if (attributeLocation === -1) {
    throw 'Can not find attribute ' + name + '.';
  }
  return attributeLocation;
}

function getUniformLocation(program, name) {
  var attributeLocation = gl.getUniformLocation(program, name);
  if (attributeLocation === -1) {
    throw 'Can not find uniform ' + name + '.';
  }
  return attributeLocation;
}

function createAndSetupTexture(gl) {
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // Set up texture so we can render any size
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

  return texture;
}

//****************** Create shaders ******************

//Create vertex and fragment shaders
var flameVertexShader = compileShader(flameVertexSource, gl.VERTEX_SHADER);
var flameFragmentShader = compileShader(flameFragmentSource, gl.FRAGMENT_SHADER);

var combineVertexShader = compileShader(combineVertexSource, gl.VERTEX_SHADER);
var combineFragmentShader = compileShader(combineFragmentSource, gl.FRAGMENT_SHADER);

var x_blurVertexShader = compileShader(blurXVertexSource, gl.VERTEX_SHADER);
var x_blurFragmentShader = compileShader(blurFragmentSource, gl.FRAGMENT_SHADER);

var y_blurVertexShader = compileShader(blurYVertexSource, gl.VERTEX_SHADER);
var y_blurFragmentShader = compileShader(blurFragmentSource, gl.FRAGMENT_SHADER);

var brightVertexShader = compileShader(brightVertexSource, gl.VERTEX_SHADER);
var brightFragmentShader = compileShader(brightFragmentSource, gl.FRAGMENT_SHADER);

// Create shader programs
var flame_program = gl.createProgram();
gl.attachShader(flame_program, flameVertexShader);
gl.attachShader(flame_program, flameFragmentShader);
gl.linkProgram(flame_program);

var x_blur_program = gl.createProgram();
gl.attachShader(x_blur_program, x_blurVertexShader);
gl.attachShader(x_blur_program, x_blurFragmentShader);
gl.linkProgram(x_blur_program);

var y_blur_program = gl.createProgram();
gl.attachShader(y_blur_program, y_blurVertexShader);
gl.attachShader(y_blur_program, y_blurFragmentShader);
gl.linkProgram(y_blur_program);

var bright_program = gl.createProgram();
gl.attachShader(bright_program, brightVertexShader);
gl.attachShader(bright_program, brightFragmentShader);
gl.linkProgram(bright_program);

var combine_program = gl.createProgram();
gl.attachShader(combine_program, combineVertexShader);
gl.attachShader(combine_program, combineFragmentShader);
gl.linkProgram(combine_program);

//Set up rectangle covering entire canvas 
var vertexData = new Float32Array([
    -1.0,  1.0, // top left
    -1.0, -1.0, // bottom left
    1.0,  1.0, // top right
    1.0, -1.0, // bottom right
    ]);

//Create vertex buffer
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);

// To make the geometry information available in the shader as attributes, we
// need to tell WebGL what the layout of our data in the vertex buffer is.
var positionHandle = getAttribLocation(flame_program, 'position');

gl.enableVertexAttribArray(positionHandle);
gl.vertexAttribPointer(positionHandle,
    2, // position is a vec2 (2 values per component)
    gl.FLOAT, // each component is a float
    false, // don't normalize values
    2 * 4, // two 4 byte float components per vertex (32 bit float is 4 bytes)
    0 // how many bytes inside the buffer to start from
    );

//Set uniform handles
var timeHandle = getUniformLocation(flame_program, 'time');
var typeHandle = getUniformLocation(flame_program, 'type');
var widthHandle = getUniformLocation(x_blur_program, 'width');
var heightHandle = getUniformLocation(y_blur_program, 'height');
var srcLocation = gl.getUniformLocation(combine_program, "srcData");
var blurLocation = gl.getUniformLocation(combine_program, "blurData");
var brightLocation = gl.getUniformLocation(bright_program, "brightData");
var bloomHandle = gl.getUniformLocation(bright_program, "bloom");

//Create and bind frame buffer
var flameFramebuffer = gl.createFramebuffer();
flameFramebuffer.width = canvas.width;
flameFramebuffer.height = canvas.height;
gl.bindFramebuffer(gl.FRAMEBUFFER, flameFramebuffer);

//Create and bind texture
var flameTexture = createAndSetupTexture(gl);

//Allocate/send over empty texture data
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, flameFramebuffer.width, flameFramebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

//Assign texture as framebuffer colour attachment
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, flameTexture, 0);

var blurFBO = [];
var blurTexture = [];

for(i = 0; i < 2; i++){
  var framebuffer = gl.createFramebuffer();
  framebuffer.width = canvas.width;
  framebuffer.height = canvas.height;
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  var texture = createAndSetupTexture(gl);

  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, framebuffer.width, framebuffer.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

  blurFBO.push(framebuffer);
  blurTexture.push(texture);
}

//****************** Main render loop ******************

//WebGL works like a state machine. Data is read from the last texture that was bound,
//using bindTexture, and written into the last framebuffer that was bound,
//using bindFramebuffer (and thereby into the texture bound to that frame buffer). 
//Binding to null displays onto the canvas (which is treated as a texture). 

var iterations = 0;

function step(){
	
	canvas.width = window.innerWidth;
	canvas.height = window.innerHeight;

  //Unbind any textures
  gl.bindTexture(gl.TEXTURE_2D, null);
  gl.activeTexture(gl.TEXTURE0);

  if(toggle_render || (iterations == 0)){
    //Update time
    time -= dt;

    //Draw flames
    gl.useProgram(flame_program);
    //Send time to program
    gl.uniform1f(timeHandle, time);
    gl.uniform1i(typeHandle, type);
    //Render to texture
    gl.bindFramebuffer(gl.FRAMEBUFFER, flameFramebuffer);
    //Draw a triangle strip connecting vertices 0-4
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    //Bright pass filter to select only light pixels
    gl.useProgram(bright_program);
    gl.uniform1f(bloomHandle, bloom);
    gl.bindFramebuffer(gl.FRAMEBUFFER, blurFBO[0]);
    gl.bindTexture(gl.TEXTURE_2D, flameTexture);
    //Draw a triangle strip connecting vertices 0-4
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    //Blur the result of the bright filter
    for(i = 1; i < blurCount; i++){
      gl.useProgram(x_blur_program);
      gl.uniform1f(widthHandle, WIDTH/(i * blurFactor));
      gl.bindFramebuffer(gl.FRAMEBUFFER, blurFBO[1]);
      gl.bindTexture(gl.TEXTURE_2D, blurTexture[0]);
      //Draw a triangle strip connecting vertices 0-4
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

      gl.useProgram(y_blur_program);
      gl.uniform1f(heightHandle, HEIGHT/(i * blurFactor));
      gl.bindFramebuffer(gl.FRAMEBUFFER, blurFBO[0]);
      gl.bindTexture(gl.TEXTURE_2D, blurTexture[1]);
      //Draw a triangle strip connecting vertices 0-4
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    }

    //Combine original and blurred image 
    gl.useProgram(combine_program);

    //Draw to canvas
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

    gl.uniform1i(srcLocation, 0);  // texture unit 0
    gl.uniform1i(blurLocation, 1);  // texture unit 1
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, flameTexture);
    gl.activeTexture(gl.TEXTURE1);
    gl.bindTexture(gl.TEXTURE_2D, blurTexture[0]);

    //Draw a triangle strip connecting vertices 0-4
    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
  }

  iterations = 1;
  requestAnimationFrame(step);
}

step();
            
          
!
999px
Loading ..................

Console