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…