Dense organic textures of spongey subaquatic coral.
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]
const options = {
grid: 20,
strokeWidth: 40,
coverage: 0.1,
iterations: 8,
blurRadius: 8,
limit: 500,
}
options.offset = options.strokeWidth * 1.1
let points = null
let strokeWidth = options.strokeWidth
const iteration = (index) => [
() => {
points = []
const ow = canvas.width - options.offset * 2
const oh = canvas.height - options.offset * 2
random
.shuffle(helpers.makeGrid(ow, oh, options.grid))
.filter(() => random.maybe(options.coverage))
.forEach((point) => {
const t = random.number(Math.PI)
const x = Math.cos(t) * (options.grid / 3)
const y = Math.sin(t) * (options.grid / 3)
points.push({
x: options.offset + point.x - x,
y: options.offset + point.y - y,
})
points.push({
x: options.offset + point.x + x,
y: options.offset + point.y + y,
})
})
},
() => {
const t = 1 - (options.iterations - index) / (options.iterations * 10)
const remaining = [points]
let count = 0
strokeWidth = options.strokeWidth * t
ctx.beginPath()
const fn = function (p0) {
if (
remaining.length < 4 ||
count > options.limit ||
(p0.y >= canvas.height - options.offset * 2 && random.maybe(0.15)) ||
(p0.x >= canvas.width - options.offset * 2 && random.maybe(0.15))
)
return
const [p1, p2] = helpers.nearest(remaining, p0)
remaining.splice(remaining.indexOf(p2), 1)
if (
helpers.sqdist(p1, p2) <
options.strokeWidth * options.strokeWidth * 10
) {
ctx.moveTo(p0.x, p0.y)
ctx.lineTo(p1.x, p1.y)
ctx.lineTo(p2.x, p2.y)
}
count++
}
points.forEach(fn)
},
() => {
ctx.lineCap = "round"
ctx.lineJoin = "round"
ctx.globalCompositeOperation = "source-atop"
ctx.lineWidth = strokeWidth * 1.5
ctx.strokeStyle = "rgba(0, 0, 0, 0.7)"
ctx.stroke()
ctx.globalCompositeOperation = "source-over"
ctx.lineWidth = strokeWidth
ctx.strokeStyle = palette.dark
ctx.stroke()
ctx.strokeStyle = palette.canvas
ctx.lineWidth = strokeWidth * 0.5
ctx.stroke()
},
() => {
posterize({
ctx,
colors,
radius: options.blurRadius,
ramp: options.blurRadius * 2,
overshoot: 0.5,
})
},
]
const steps = Array.from({ length: options.iterations }, (e, i) =>
iteration(i)
)
return sequence([steps.flat(), () => badge(ctx, { colors })])
}