Chaikin Curves: Creating Smooth Lines in Generative Art

Created April 9, 2025 (Today)Updated April 9, 2025 (Today)

Chaikin curves, developed by George Chaikin in 1974, offer a simple yet powerful way to create smooth curves from a series of points. In generative art, they provide an elegant method for transforming rigid, angular lines into flowing, organic shapes.

What are Chaikin Curves?

At its core, Chaikin's algorithm is a corner-cutting method. It works by repeatedly replacing each line segment with two new points that are positioned a fraction of the way in from each end (typically 1/4 and 3/4 along the line). After several iterations, this process transforms a polygonal line into a smooth curve.

Note: The final result is shorter than the original line.

The Algorithm

Here's a simple (recursive, so not simple) implementation of the Chaikin curve algorithm:

// points are defined as [[x, y], [x, y], ...]
function chaikinCurve(points, iterations) {
    if (iterations === 0) return points

    let newPoints = []
    for (let i = 0; i < points.length - 1; i++) {
        const [p0x, p0y] = points[i]
        const [p1x, p1y] = points[i + 1]

        // Create two new points at 1/4 and 3/4 along the line
        const q0 = [
            p0x * 0.75 + p1x * 0.25, //
            p0y * 0.75 + p1y * 0.25,
        ]

        const q1 = [
            p0x * 0.25 + p1x * 0.75, //
            p0y * 0.25 + p1y * 0.75,
        ]

        newPoints.push(q0, q1)
    }

    return chaikinCurve(newPoints, iterations - 1)
}

Animated Explanation

Generative Art Examples

Subtitle: Sine Blob

Subtitle: Joy Division

import { Line, Polyline, Polygon, asPoints } from 'root/geo'
import { random } from 'root/random'
import { zip, interleave } from 'root/array'
import { chaikinCurve } from 'root/algos'

function joyDivision(cmd, palette) {
    // ext.chaikinCurve()

    // make 2 vertical control lines
    const lineA = new Line([-0.9, 0.7], [-0.9, -0.9])
    const lineB = new Line([0.9, 0.7], [0.9, -0.9])

    // turn each line into endpoints for horizontal lines
    const numLines = 32
    const ptsA = asPoints(lineA, numLines)
    const ptsB = asPoints(lineB, numLines)

    // zip up the points into lines
    const lines = zip(ptsA, ptsB).map(([ptA, ptB]) => new Line(ptA, ptB))

    // convert the new lines into N points we can move around
    const numPoints = 36
    const linePoints = lines.map((line) => asPoints(line, numPoints))

    // randomly move the points (only on the positive Y axis)
    const randomnessBig = 0.4
    const randomnessSmall = 0.005
    let linePointsMoved = linePoints.map((pts) =>
        pts.map((pt) => {
            const x = pt[0]
            const gaussShape = Math.exp(-8 * x * x)
            return [pt[0], pt[1] + gaussShape * random(0, randomnessBig)]
        })
    )

    // smooth out curve one iteration
    let smoothPoints = linePointsMoved.map((pts) => chaikinCurve(pts, 2))

    // second pass of random displacement, smaller values
    linePointsMoved = smoothPoints.map((pts) =>
        pts.map((pt) => {
            // (TRICKY BIT)
            // TODO: learn how these work
            // gaussian shaping function
            // only apply on the outsides
            const x = pt[0]
            const gaussShape = 1.0 - Math.exp(-8 * x * x)
            return [pt[0], pt[1] + random(-randomnessSmall, gaussShape * randomnessSmall)]
        })
    )

    // finish smoothing things out
    // smoothPoints = linePointsMoved.map((pts) => chaikinCurve(pts, 2))

    // convert to polylines
    cmd.clear(palette.background)

    const polylines = smoothPoints.map((pts) => new Polyline(pts, { stroke: palette.secondary, weight: 0.008 }))
    const polys = polylines.map((polyline) => new Polygon(polyline.pts, { fill: palette.background }))
    cmd.draw(interleave(polys, polylines))
}
joyDivision.title = 'Joy Division'
export { joyDivision }

// const meta = {
// title: 'Joy Division',
// description: 'This is way more complex than needed, probably better to use loops like in the original example.',
// refLink: 'https://generativeartistry.com/tutorials/joy-division/',
// }

Subtitle: Rats Nest

References