const vert = `attribute vec3 aPosition;
attribute vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat3 uNormalMatrix;
varying highp vec4 vVertColor;
vec4 positionVec4 = vec4(aPosition, 1.0);
gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4;
vVertColor = aVertexColor;
const frag = `precision mediump float;
varying highp vec4 vVertColor;
uniform float pixelDensity;
// https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
return fract(sin(n) * 43758.5453123);
return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
return mix(rand(fl), rand(fl + 1.0), fc);
const vec2 d = vec2(0.0, 1.0);
vec2 f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
// Rotate the position (which is used to get a noise value) based on the position
// to make the noise less repetitive
) * (gl_FragCoord.xy / pixelDensity);
vec3 fullColor = vVertColor.xyz;
float threshold = 1. - vVertColor.w * 1.05;
float opacity = smoothstep(
fract(10. * noise(coord * 0.2) + 5. * noise(coord * 1.1))
gl_FragColor = vec4(fullColor, 1.0) * opacity;
createCanvas(500, 500, WEBGL)
setAttributes({ antialias: true })
const container = document.getElementById('album')
const canvas = document.querySelector('canvas')
canvas.parentElement.removeChild(canvas)
container.appendChild(canvas)
noiseGradient = createShader(vert, frag)
function gradientStroke(w, h, colors) {
const numPoints = max(2, colors.length)
beginShape(TRIANGLE_STRIP)
for (let i = 0; i < numPoints; i++) {
const y = map(i, 0, numPoints - 1, -h / 2, h / 2)
fill(colors[min(i, colors.length - 1)])
translate(width * 0.7, -height * 0.6)
rotate(random(0.1, 0.4) * PI)
noiseGradient.setUniform('seed', random() * 1000)
gradientStroke(width / 15, random(width, width * 2.8), [
color(209, 121, 110, 230),
color(247, 203, 106, 230),
function drawTree(x, z) {
const h = map(z, 0, 1, height * 0.75, 1.5 * height)
const w = map(z, 0, 1, width * 0.025, width * 0.125)
const r = random(-0.015, 0.015) * PI
translate(x, height / 2 - h / 2)
for (let i = 0.3; i < 0.7; i += 0.1) {
if (random() < 0.1) continue
translate(0, h * (0.5 - i))
scale(random([1, -1]), 1)
rotate(random(PI * 0.55, PI * 0.75))
const branchLength = random(0.25*w, 3*w)
translate(0, branchLength / 2)
gradientStroke(w/6, branchLength, [
color(55, 56, 123, 230 * 2/3),
color(55, 56, 123, 230 * 1/3),
translate(0, random(-0.25, 0.25) * branchLength)
scale(random([1, -1]), 1)
rotate(random(PI * 0.2, PI * 0.35))
const subBranchLength = random(0.25*w, 0.75*w)
translate(0, subBranchLength / 2)
gradientStroke(w/8, subBranchLength, [
color(55, 56, 123, 200 * 2/3),
color(55, 56, 123, 200 * 1/3),
noiseGradient.setUniform('pixelDensity', pixelDensity())
drawingContext.disable(drawingContext.DEPTH_TEST)
drawingContext.enable(drawingContext.BLEND)
drawingContext.blendFunc(drawingContext.ONE, drawingContext.ONE_MINUS_SRC_ALPHA)
translate(0, -height * 0.25)
gradientStroke(width, height * 0.5, [
translate(0, height * 0.25)
gradientStroke(width, height * 0.5, [
translate(0, height * 0.25)
gradientStroke(width, height * 0.5, [
for (let i = 0; i < 25; i++) {
x: random(-width / 2, width / 2),
x: random(-width / 2, width / 2),
x: objects[objects.length-1].x +
random([-1, 1]) * width * random(0.1, 0.5),
for (let i = 0; i < 6; i++) {
const z = pow(random(), 0.5)
for (let j = 0; j < 5; j++) {
objects.sort((a, b) => a.z - b.z)
for (const { type, z, x } of objects) {