createCanvas(windowWidth, windowHeight)
capture = createCapture(VIDEO, { flipped: true })
edgesFilter = createFilterShader(`
float resX = 0., resY = 0.;
for (int xOff = -2; xOff <= 2; xOff++) {
for (int yOff = -2; yOff <= 2; yOff++) {
float c = texture2D(tex0, vTexCoord + vec2(float(xOff), float(yOff))/canvasSize).r;
gl_FragColor = vec4(vec3(1. - smoothstep(0., 1., max(resX, resY))), 1.);
const reveal = map(millis(), startTime, startTime + 4000, 0, 1, true)
const numPts = Math.ceil(path.getTotalLength() * reveal)
for (let i = 0; i <= numPts; i++) {
const pt = path.getPointAtLength(map(i, 0, numPts, 0, path.getTotalLength() * reveal))
image(capture, 0, 0, width, height, 0, 0, capture.width, capture.height, COVER)
textAlign(CENTER, CENTER)
text('Click anywhere to turn into lines', width/2, height*0.7)
function mousePressed() {
image(capture, 0, 0, width, height, 0, 0, capture.width, capture.height, COVER)
for (let i = 0; i < 30000; i++) {
const x = floor(random(width))
const y = floor(random(height))
if (img.pixels[(y*width + x) * 4] + random(-1,1)*40 < 255/2) {
samples.push(createVector(x, y))
const candidates = samples.map(p => [p])
while (candidates.length > 1) {
const string = candidates.shift()
const dists = candidates.map((other) => {
string.at(0).dist(other.at(0)),
string.at(0).dist(other.at(-1)),
string.at(-1).dist(other.at(0)),
string.at(-1).dist(other.at(-1)),
const minDist = min(dists)
return { minDist, dists }
const minDist = min(dists.map(d => d.minDist))
const idx = dists.map(d => d.minDist).indexOf(minDist)
const [other] = candidates.splice(idx, 1)
const distIdx = dists[idx].dists.indexOf(minDist)
candidates.push([...other.reverse(), ...string])
} else if (distIdx === 1) {
candidates.push([...other, ...string])
} else if (distIdx === 2) {
candidates.push([...string, ...other])
candidates.push([...string, ...other.reverse()])
for (let i = 0; i < 4; i++) {
pts = pts.map((p, i) => pts[i].copy().add(pts.at(i-1)).add(pts.at(i+1)||pts[0]).mult(1/3))
const tangents = pts.slice(1).map((pt, i) => pt.copy().sub(pts[i]))
tangents.push(tangents.at(-1).copy())
path = BezierPath.create(candidates[0].map((pt, i) => ({
left: pt.copy().sub(tangents[i].copy().mult(0.4)),
right: pt.copy().add(tangents[i].copy().mult(0.4)),