createCanvas(600, 600, WEBGL)
for (let i = 0; i < 40; i++) {
fbo = new Framebuffer(window)
blurShader = createShader(vert, frag)
const scale = max(20, randomGaussian(60, 40))
randomGaussian(100, width/5),
randomGaussian(0, width/5)
verts: [0,1,2].map((i) => createVector(
scale * cos(TWO_PI*i/3 + randomGaussian(0, 0.4)),
scale * sin(TWO_PI*i/3 + randomGaussian(0, 0.4))
floorOffset: createVector(
randomGaussian(-100, 80),
function distort(position, time) {
const noiseOffset = createVector(
20*sin(0.01*position.x + time + 0.05*position.y),
30*sin(20 + 0.02*position.x + time*0.8 + 0.07*position.y)
return position.copy().add(noiseOffset)
const eyeZ = (height/2) / tan(PI/6)
perspective(PI/3, width/height, near, far)
const blurIntensity = 0.06
drawingContext.disable(drawingContext.DEPTH_TEST)
drawingContext.enable(drawingContext.BLEND)
drawingContext.blendEquation(drawingContext.FUNC_ADD)
drawingContext.blendFunc(drawingContext.SRC_ALPHA, drawingContext.DST_ALPHA)
const time = millis()/1000
beginShape(TRIANGLE_STRIP)
for (const x of [-width/2, width/2]) {
for (const { position, verts, floorOffset } of shapes) {
const distortedVerts = verts
.map((v) => distort(v.copy().add(position), time))
const floorVerts = distortedVerts
.map((v) => v.copy().sub(position).mult(4).add(position).add(floorOffset))
for (const { x, y, z} of distortedVerts) {
for (const { x, y, z } of floorVerts) {
beginShape(TRIANGLE_STRIP)
for (let i = 0; i < 4; i++) {
for (const [alpha, { x, y, z }] of [
[3, distortedVerts[idx]],
fill(254, 255, 217, alpha)
_renderer.getTexture(fbo.depth).setInterpolation(
blurShader.setUniform('uImg', fbo.color)
blurShader.setUniform('uDepth', fbo.depth)
blurShader.setUniform('uSize', [width, height])
blurShader.setUniform('uIntensity', blurIntensity)
blurShader.setUniform('uNumSamples', 25)
blurShader.setUniform('uTargetZ', targetDepth)
blurShader.setUniform('uNear', near)
blurShader.setUniform('uFar', far)
rect(0, 0, width, -height)