Shader Lavalamp effect
Materials from Atcom's tech fest Atcom Next | Beyond Digital https://next.atcom.gr/
Shader based on Jamie Wong implementation http://jamie-wong.com/2016/07/06/metaballs-and-webgl/
<script type="x-shader/x-vertex" id="vertexMetaballs"> | |
attribute vec2 position; | |
void main() { | |
gl_Position = vec4(position, 0.0, 1.0); | |
} | |
</script> | |
<script type="x-shader/x-fragment" id="fragmentMetaballs"> | |
precision highp float; | |
const int NUM_METABALLS = 15; | |
uniform vec3 metaballs[15]; | |
uniform vec2 uResolution; | |
uniform sampler2D uColorSampler; | |
uniform sampler2D uNoiseSampler; | |
uniform float uTime; | |
void main(){ | |
float x = gl_FragCoord.x; | |
float y = gl_FragCoord.y; | |
float v = 0.0; | |
float radius = 2.0; | |
float speed = 1.5; | |
for (int i = 0; i < NUM_METABALLS; i++) { | |
vec3 mb = metaballs[i]; | |
float dx = mb.x - x; | |
float dy = mb.y - y; | |
float r = mb.z; | |
v += r*r/(dx*dx + dy*dy); | |
} | |
vec4 color; | |
if (v > 1.0) { | |
vec4 textureColor = texture2D(uColorSampler, vec2(gl_FragCoord.x / uResolution.x, gl_FragCoord.y / uResolution.y) ); | |
vec4 noiseColor = (texture2D(uNoiseSampler, gl_FragCoord.xy / 100.0 )) / 1.; | |
float l = length(noiseColor); | |
if(l > 1.05){ | |
vec4 mixedColor = textureColor + (noiseColor * 0.001); | |
color = mixedColor; | |
} | |
else{ | |
//discard; | |
color = textureColor * 0.85; | |
} | |
} | |
else { | |
discard; | |
} | |
gl_FragColor = vec4(color.rgb, 0.8); | |
} | |
</script> | |
<div class="wrapper grad"> | |
<div class="dots"></div> | |
<canvas id="metaball-canvas" class="metaball-canvas"></canvas> | |
</div> | |
var canvas; | |
var gl; | |
var realToCSSPixels = window.devicePixelRatio; | |
var displayWidth; | |
var displayHeight; | |
var rings; | |
var createdMetaballs = []; | |
var assetsIndexToLoad = 0; | |
var assetsToLoad = [ | |
{path: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-183/', src: 'noise3.png', name: 'noise3', type: 'texture'} | |
]; | |
var assets = {}; | |
window.onload = preloadAssets; | |
function preloadAssets() { | |
function checkIfAllAssetsAreLoaded() { | |
if (assetsIndexToLoad < assetsToLoad.length) { | |
loadAssetIndex(assetsIndexToLoad); | |
} | |
else { | |
initialize(); | |
} | |
} | |
function loadAssetIndex(index) { | |
var objectToLoad = assetsToLoad[index]; | |
switch (objectToLoad.type) { | |
case 'texture': | |
var image = new Image(); | |
image.onload = function(event) { | |
assets[objectToLoad.name] = this; | |
assetsIndexToLoad++; | |
checkIfAllAssetsAreLoaded(); | |
}; | |
image.crossOrigin = ''; | |
image.src = objectToLoad.path + objectToLoad.src; | |
break; | |
} | |
} | |
loadAssetIndex(assetsIndexToLoad); | |
} | |
function initialize(){ | |
canvas = document.getElementById('metaball-canvas'); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
var glConfig = { | |
premultipliedAlpha: true, | |
antialias: true, | |
depth:true, | |
alpha: true | |
} | |
gl = canvas.getContext('webgl', glConfig) || canvas.getContext('experimental-webgl', glConfig); | |
if(!gl){ | |
console.error('cannot find gl', gl); | |
return; | |
} | |
displayWidth = Math.floor(gl.canvas.clientWidth * realToCSSPixels); | |
displayHeight = Math.floor(gl.canvas.clientHeight * realToCSSPixels); | |
var minSpeed = 0.2; | |
var maxSpeed = 2.5; | |
var minMultiplierArcX = -.25; | |
var maxMultiplierArcX = .75; | |
var minMultiplierArcY = -.25; | |
var maxMultiplierArcY = .25; | |
var scale = 1.0; | |
var metaballsGroup1 = { | |
metaballs:[ | |
{ centerOffsetX:26 * scale, centerOffsetY:155 * scale, radius: 70 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-110 * scale, centerOffsetY:10 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:12 * scale, centerOffsetY:-114 * scale, radius: 48 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-300 * scale, centerOffsetY:20 * scale, radius: 160 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-570 * scale, centerOffsetY:-20 * scale, radius: 50 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.2}, {color:'#c8246c', stop:.35}, {color:'#40204c', stop:.55}, {color:'#e24926', stop:.75}, {color:'#40204c', stop:1.0}], false, false) | |
}; | |
var metaballsGroup2 = { | |
metaballs:[ | |
{ centerOffsetX:-290 * scale, centerOffsetY:60 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-100 * scale, centerOffsetY:45 * scale, radius: 70 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-60 * scale, centerOffsetY:60 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:160 * scale, centerOffsetY:170 * scale, radius: 90 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:310 * scale, centerOffsetY:40 * scale, radius: 40 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:450 * scale, centerOffsetY:-120 * scale, radius: 50 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:230 * scale, centerOffsetY:-240 * scale, radius: 70 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:320 * scale, centerOffsetY:-130 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:110 * scale, centerOffsetY:-70 * scale, radius: 80 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-1070 * scale, centerOffsetY:-500 * scale, radius: 20 * scale, speed: getRandomFloat(0.07, 0.014), t:0.0, arcMultiplierX:getRandomFloat(30.0, 30.0), arcMultiplierY:getRandomFloat(10.0, 10.0) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.0}, {color:'#e24926', stop:0.3}, {color:'#c8246c', stop:.4}, {color:'#40204c', stop:.7}], true, false) | |
}; | |
var metaballsGroup3 = { | |
metaballs:[ | |
{ centerOffsetX:410 * scale, centerOffsetY:-120 * scale, radius: 18 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:340 * scale, centerOffsetY:-200 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:200 * scale, centerOffsetY:-190 * scale, radius: 40 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:250 * scale, centerOffsetY:-280 * scale, radius: 16 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.56}, {color:'#c8246c', stop:.63}, {color:'#40204c', stop:.7}], false, false) | |
}; | |
var metaballsGroup4 = { | |
metaballs:[ | |
{ centerOffsetX:-410 * scale, centerOffsetY:-270 * scale, radius: 28 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-490 * scale, centerOffsetY:-230 * scale, radius: 34 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-470 * scale, centerOffsetY:-320 * scale, radius: 40 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-470 * scale, centerOffsetY:320 * scale, radius: 40 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-430 * scale, centerOffsetY:360 * scale, radius: 30 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.1}, {color:'#c8246c', stop:.20}, {color:'#40204c', stop:.4}], false, false) | |
}; | |
var metaballsGroup5 = { | |
metaballs:[ | |
{ centerOffsetX:-500 * scale, centerOffsetY:-100 * scale, radius: 24 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:30 * scale, centerOffsetY:-120 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:480 * scale, centerOffsetY:170 * scale, radius: 21 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.25}, {color:'#c8246c', stop:.60}, {color:'#40204c', stop:0.78}], true, false) | |
}; | |
var metaballsGroup6 = { | |
metaballs:[ | |
{ centerOffsetX:820 * scale, centerOffsetY:20 * scale, radius: 200 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:480 * scale, centerOffsetY:30 * scale, radius: 70 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:500 * scale, centerOffsetY:-10 * scale, radius: 65 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:1080 * scale, centerOffsetY:30 * scale, radius: 35 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:400 * scale, centerOffsetY:160 * scale, radius: 55 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:350 * scale, centerOffsetY:-120 * scale, radius: 75 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:1670 * scale, centerOffsetY:500 * scale, radius: 15 * scale, speed: getRandomFloat(0.21, 0.22), t:13.0, arcMultiplierX:30.0, arcMultiplierY:6.0 }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.0}, {color:'#e24926', stop:0.7}, {color:'#c8246c', stop:.8}, {color:'#40204c', stop:1.0}], false, false) | |
}; | |
var metaballsGroup7 = { | |
metaballs:[ | |
{ centerOffsetX:-930 * scale, centerOffsetY:40 * scale, radius: 30 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-800 * scale, centerOffsetY:90 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-640 * scale, centerOffsetY:270 * scale, radius: 50 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-590 * scale, centerOffsetY:150 * scale, radius: 90 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-400 * scale, centerOffsetY:240 * scale, radius: 40 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-300 * scale, centerOffsetY:120 * scale, radius: 35 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-450 * scale, centerOffsetY:50 * scale, radius: 70 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-590 * scale, centerOffsetY:-40 * scale, radius: 60 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
{ centerOffsetX:-370 * scale, centerOffsetY:-70 * scale, radius: 50 * scale, speed: getRandomFloat(minSpeed, maxSpeed), t:Math.random() * 200, arcMultiplierX:getRandomFloat(minMultiplierArcX, maxMultiplierArcX), arcMultiplierY:getRandomFloat(minMultiplierArcY, maxMultiplierArcY) }, | |
], | |
texture:generateGradientTexture([{color:'#e24926', stop:0.2}, {color:'#c8246c', stop:.4}, {color:'#40204c', stop:.7}], true, false) | |
}; | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup6)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup7)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup2)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup1)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup3)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup4)); | |
createdMetaballs.push(new Metaballs(gl, metaballsGroup5)); | |
for (var i = 0; i < createdMetaballs.length; i++) { | |
setTimeout(createdMetaballs[i].fadeIn, i * 200); | |
}; | |
window.addEventListener('resize', onWindowResize); | |
window.addEventListener('mousemove', onWindowMouseMove); | |
resizeGL(gl); | |
step(); | |
} | |
function generateGradientTexture(colors, vertical, debug) { | |
colors = colors || [{color:'#000000', stop:0.0}, {color:'#FFF000', stop:.5}, {color:'#642054', stop:1.0}]; | |
vertical = vertical !== undefined ? vertical : false; | |
var size = 512; | |
// create canvas | |
var textureCanvas = document.createElement( 'canvas' ); | |
textureCanvas.width = size; | |
textureCanvas.height = size; | |
if(debug == true){ | |
textureCanvas.style.position = 'absolute'; | |
textureCanvas.style.top = '0px'; | |
textureCanvas.style.left = '0px'; | |
document.body.appendChild(textureCanvas); | |
} | |
// get context | |
var context = textureCanvas.getContext( '2d' ); | |
// draw gradient | |
context.rect( 0, 0, size, size ); | |
var grd = vertical ? context.createLinearGradient(0, size, 0, 0) : context.createLinearGradient(0, 0, size, 0); | |
for(var i = 0; i < colors.length; i++){ | |
grd.addColorStop(colors[i].stop, colors[i].color); | |
} | |
context.fillStyle = grd; | |
context.fillRect(0, 0, size, size); | |
return textureCanvas; | |
} | |
function getRandomFloat(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
function onWindowResize(event){ | |
canvas.width = canvas.clientWidth; | |
canvas.height = canvas.clientHeight; | |
resizeGL(gl); | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); | |
} | |
function onWindowMouseMove(event){ | |
createdMetaballs.forEach(function(metaball){ | |
metaball.handleMouseMove(event.clientX, event.clientY); | |
}); | |
} | |
function resizeGL(gl) { | |
realToCSSPixels = window.devicePixelRatio; | |
// Lookup the size the browser is displaying the canvas in CSS pixels | |
// and compute a size needed to make our drawingbuffer match it in | |
// device pixels. | |
displayWidth = Math.floor(gl.canvas.clientWidth * realToCSSPixels); | |
displayHeight = Math.floor(gl.canvas.clientHeight * realToCSSPixels); | |
// Check if the canvas is not the same size. | |
if (gl.canvas.width !== displayWidth || | |
gl.canvas.height !== displayHeight) { | |
// Make the canvas the same size | |
gl.canvas.width = displayWidth; | |
gl.canvas.height = displayHeight; | |
} | |
gl.viewport(0, 0, displayWidth, displayHeight); | |
createdMetaballs.forEach(function(metaball){ | |
metaball.handleResize(displayWidth, displayHeight); | |
}); | |
} | |
var step = function() { | |
createdMetaballs.forEach(function(metaball){ | |
metaball.updateSimulation(); | |
}); | |
requestAnimationFrame(step); | |
}; | |
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-183/Metaballs.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js"></script> |
body { | |
background-color: #101010; | |
color: #fff; | |
margin: 0px; | |
overflow: hidden; | |
} | |
.dots{ | |
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-183/dots-transparent.png); | |
width: 100%; | |
height:100%; | |
opacity: .1; | |
background-repeat: repeat; | |
} | |
.wrapper{ | |
position:absolute; | |
top:0px; | |
left:0px; | |
width: 100%; | |
height:100%; | |
text-align: center; | |
} | |
.grad { | |
background: red; /* For browsers that do not support gradients */ | |
background: -webkit-linear-gradient(left top, #152a8e, #b1376c); /* For Safari 5.1 to 6.0 */ | |
background: -o-linear-gradient(bottom right, #152a8e, #b1376c); /* For Opera 11.1 to 12.0 */ | |
background: -moz-linear-gradient(bottom right, #152a8e, #b1376c); /* For Firefox 3.6 to 15 */ | |
background: linear-gradient(to bottom right, #152a8e, #b1376c); /* Standard syntax */ | |
} | |
.metaball-canvas{ | |
width:100%; | |
height:100%; | |
position: absolute; | |
top:0; | |
left:0; | |
} |
Shader Lavalamp effect
Materials from Atcom's tech fest Atcom Next | Beyond Digital https://next.atcom.gr/
Shader based on Jamie Wong implementation http://jamie-wong.com/2016/07/06/metaballs-and-webgl/