Current
a field of parallel undulating lines that shimmer, after Riley's Current (1964). Each line is a smooth drawCurve through sampled points.
- Run
swift run --package-path Examples Example-Recreations-BridgetRiley-Current- Source
- Examples/Recreations/BridgetRiley/Current/Sketch.swift
// Recreation after Bridget Riley — "Current" (1964), synthetic polymer paint on
// composition board (MoMA): the whole surface filled with closely spaced,
// parallel undulating lines whose wavelength tightens toward the center, so the
// eye reads a rippling, shimmering field. An original Ollin interpretation built
// from the artwork — not ported from a source sketch. A homage, not a
// reproduction, and not affiliated with or endorsed by the artist.
import Foundation
import Ollin
/// A field of parallel wavy lines after Bridget Riley's Op-art "Current" (1964).
///
/// Each line is a smooth curve drawn straight from a handful of sampled points
/// with `drawCurve` — Ollin reads the points and fairs a Catmull-Rom spline
/// through them, so a sine sampled coarsely comes back as a clean wave with no
/// tessellation on our part.
///
/// The shimmer comes from two devices true to the painting:
/// 1. the horizontal **wavelength tightens toward the vertical center** (the
/// waves look like they speed up through the middle), and
/// 2. the whole field **drifts** sideways over time, which Riley's still board
/// only implies but motion makes literal.
@main
final class Current: Sketch {
@Param(20...140) var lines = 86.0 // number of horizontal lines
@Param(0...40) var amplitude = 15.0 // wave height (px, before scale)
@Param(1...18) var waves = 7.0 // waves across the width at the edges
@Param(0...2) var swell = 1.0 // extra waves through the center
@Param(0...4) var drift = 0.7 // sideways drift speed
override func setup() {
noFill()
}
override func draw() {
background(.white)
stroke(.black)
strokeWeight(1.6 * scale)
// All lengths scale with the canvas (`scale` = shortSide / 1000),
// so the line field keeps its density at any export size.
let inset = 70 * scale
let left = inset, right = width - inset
let top = inset, bottom = height - inset
let centerY = (top + bottom) / 2
let halfHeight = (bottom - top) / 2
let rows = Int(lines)
let gap = (bottom - top) / Double(rows)
let amp = amplitude * scale
let samples = 60
let phase = time * drift
for r in 0...rows {
let y0 = top + Double(r) * gap
let v = (y0 - centerY) / halfHeight // -1 (top) … 1 (bottom)
// Wavelength tightens toward the middle: more cycles where |v| → 0.
let cycles = waves * (1 + swell * (1 - abs(v)))
var points: [Vector2] = []
points.reserveCapacity(samples + 1)
for s in 0...samples {
let t = Double(s) / Double(samples)
let x = left + (right - left) * t
// Reference the cycles from mid-width so the wavelength spread
// fans symmetrically out to both edges (coherent center), and
// offset each row a little so the lines never all align.
let y = y0 + amp * sin(cycles * (t - 0.5) * .tau + phase + v * 0.9)
points.append(Vector2(x, y))
}
drawCurve(points)
}
}
}