Bursts of graffiti, a chaotic array of forms and figures.
Get occasional updates about new fonts, designs and other interesting things.
import helpers from "/scratchpad/_lib/line.asset.mjs"
import sequence from "/scratchpad/_lib/sequence.asset.mjs"
import posterize from "/scratchpad/posterize/posterize.asset.mjs"
import badge from "/scratchpad/_lib/badge.asset.mjs"
import palette from "/scratchpad/_lib/palette.asset.mjs"
import * as random from "/scratchpad/_lib/random.asset.mjs"
const dpi = window.devicePixelRatio || 1
export default (canvas) => {
const scale = dpi
canvas.width = canvas.offsetWidth * scale
canvas.height = canvas.offsetHeight * scale
const ctx = canvas.getContext("2d")
const colors = [palette.dark, palette.canvas, palette.light]
const gridSize = random.integer(4, 8)
const strokeWidth = random.integer(20, 60)
const iterations = random.integer(6, 10)
const coverage = random.number(0.02)
const offset = strokeWidth * 1.1
const blurRadius = strokeWidth / 8
let points = null
const iteration = (index) => [
() => {
points = []
const ow = canvas.width - offset * 2
const oh = canvas.height - offset * 2
helpers
.makeGrid(ow, oh, gridSize)
.filter(() => random.maybe(coverage))
.forEach((point) => {
const t = random.number(Math.PI)
const x = Math.cos(t) * (gridSize / 3)
const y = Math.sin(t) * (gridSize / 3)
points.push({
x: offset + point.x - x,
y: offset + point.y - y,
})
points.push({
x: offset + point.x + x,
y: offset + point.y + y,
})
})
},
() => {
const t = 1 - (iterations - index) / (iterations * 10)
const remaining = [points].filter((p) => random.maybe(0.6))
ctx.beginPath()
const fn = function (p0) {
if (remaining.length < 4) return
const [p1, p2] = helpers.nearest(remaining, p0).slice(1)
ctx.moveTo(p1.x, p1.y)
const mx = p1.x + (p2.x - p1.x) / 2 + random.wobble(canvas.width) / 40
const my = p1.y + (p2.y - p1.y) / 2 + random.wobble(canvas.width) / 40
ctx.quadraticCurveTo(mx, my, p2.x, p2.y)
}
random.shuffle(points).forEach(fn)
ctx.globalCompositeOperation = "source-atop"
ctx.globalAlpha = 0.2
ctx.lineCap = "round"
ctx.lineWidth = strokeWidth * t * 1.5
ctx.strokeStyle = palette.dark
ctx.stroke()
ctx.globalCompositeOperation = "source-over"
ctx.globalAlpha = 1
ctx.lineWidth = strokeWidth * t
ctx.strokeStyle = palette.dark
ctx.stroke()
ctx.strokeStyle = palette.canvas
ctx.lineWidth = strokeWidth * t * 0.4
ctx.stroke()
if (random.maybe(0.9)) {
ctx.save()
ctx.translate(0, 80)
ctx.strokeStyle = palette.dark
ctx.lineWidth = strokeWidth * t * 0.1
ctx.stroke()
ctx.restore()
}
},
() => {
posterize({
ctx,
radius: blurRadius,
colors,
ramp: blurRadius * 2,
})
},
]
const instructions = Array.from({ length: iterations }, (e, i) =>
iteration(i)
).flat()
instructions.push(() => badge(ctx, { stroke: false }))
return sequence(instructions)
}