sTwister
- const canvas = document.createElement('canvas') 
- const c = canvas.getContext('2d') 
- canvas.width = innerWidth * 2 
- canvas.height = innerHeight * 2 
- canvas.style.scale = '.5 .5' 
- canvas.style.transformOrigin = '0 0' 
- canvas.style.filter = 'contrast(1.1)' 
- document.body.append(canvas) 
- const canvas2 = document.createElement('canvas') 
- const c2 = canvas2.getContext('2d') 
- canvas2.width = innerWidth * 2 
- canvas2.height = innerHeight * 2 
- const r = (n = 1) => Math.random() * n 
- let anc = ~~r(360) 
- function plaint(x, y) { 
- let vy = r(-20) - 1 
- let vx = 0 
- const cl = ~~r(50) + anc; 
- let t = r(7) 
- let rx = r(1) + .5 
- let jag = r(.5) 
- let bright = r(100) 
- return () => { 
- vx = rx * Math.cos(t) 
- if (r() < jag) { 
- t += r(3) 
- }
- x += vx
- y += vy
- c.fillStyle = `hsla(${cl}, 20%, ${bright}%, .5)` 
- c.fillRect(x, y, 4, 4) 
- if (y < 0) { 
- y = canvas.height 
- vy = r(-20) - 1 
- }
- }
- }
- function erase() { 
- c.fillStyle = 'black' 
- c.fillRect(0, 0, canvas.width, canvas.height) 
- }
- erase() 
- const plntNum = 350 
- let xStep = canvas.width / plntNum 
- const plnt = [] 
- for (let i = 0; i < plntNum; i++) { 
- const x = i * xStep 
- plnt.push(plaint(x, canvas.height)) 
- }
- let R = null 
- function swister(C, x, y, fc, ss, n, slow) { 
- const shadesNum = n || 100 
- const shades = [] 
- const rad = R || 200 
- for (let i = 0; i < shadesNum; i++) { 
- shades.push({ 
- x,
- y,
- xx: 0, 
- yy: 0, 
- r: r(rad) + 200, 
- t: r(7), 
- rinc: r(1) - .1, 
- tinc: r(.1), 
- cl: fc(), 
- a: r(.02) + .01 
- }) 
- }
- return () => { 
- for (let i = 0; i < shadesNum; i++) { 
- let s = shades[i] 
- s.xx = s.x + s.r * Math.cos(s.t) 
- s.yy = s.y + s.r * Math.sin(s.t) 
- s.r -= s.rinc; 
- if (s.r < 200) { 
- s.r = r(rad) + 200 
- s.tinc = r(.1) 
- if (slow) s.tinc *= .1 
- if (r() < .5) s.inc *= -1; 
- s.rinc = r(.1) 
- }
- s.t += s.tinc 
- C.fillStyle = `rgba(${s.cl}, ${s.cl}, ${s.cl}, ${s.a})` 
- C.fillRect(s.xx, s.yy, ss, ss) 
- }
- }
- }
- R = r(canvas.width / 5) + 50 
- let s1x = (canvas.width - R) * r() + R / 2 
- let s1y = (canvas.height - R) * r() + R / 2 
- let s = swister(c, s1x, s1y, 
- () => r(10), 24 
- )
- let s2 = swister(c2, 
- s1x - 50, s1y - 50, 
- () => r(200) + 0, 4 
- )
- let s3 = swister(c2, 
- s1x - 50, s1y - 50, 
- () => r(50) + 200, 8, 4, true 
- )
- R = r(150) + 50 
- let s2x = (canvas.width - R) * r() + R / 2 
- let s2y = (canvas.height - R) * r() + R / 2 
- let sa = swister(c, s2x, s2y, 
- () => r(10), 24 
- )
- let sb = swister(c2, 
- s2x - 50, s2y - 50, 
- () => r(200) + 0, 4 
- )
- let sc = swister(c2, 
- s2x - 50, s2y - 50, 
- () => r(50) + 200, 8, 4, true 
- )
- function draw() { 
- c.globalAlpha = 1; 
- c.globalCompositeOperation = 'source-over' 
- plnt.forEach(p => p()) 
- s() 
- sa() 
- c.globalAlpha = .05; 
- c.drawImage(canvas, -10, -10, canvas.width + 10, canvas.height + 10) 
- // c.globalCompositeOperation = 'source-atop'
- s2() 
- sb() 
- s3() 
- sc() 
- c.globalAlpha = 1 
- c.globalCompositeOperation = 'hard-light' 
- c.drawImage(canvas2, 0, 0); 
- c.globalAlpha = .02; 
- c.drawImage(canvas, 0, 0, canvas.width, canvas.height + 10) 
- requestAnimationFrame(draw) 
- }
- draw() 
Speed-coded this on the train the other day. Might port over to WebGL sometime soon…