Creating Sketches with Solandra for the Amimetic Website
I wanted to create a few 'sketches' with my open source, TypeScript-first creative coding library Solandra both as a demo and for something interesting to look at on my website. Here are their source code and explanations.
1
This shows off a couple of Solandra's features. palettePreset
returns a nice palette of colors in an array.
Then the main code converts a basic shape to a path
then uses a bunch of Solandra's powerful path manipulation functions to create interesting shapes, which can then be colored by the palette.
const cols = palettePreset('natural', 12)
s.background(350, 30, 80)
new RegularPolygon({ at: s.meta.center, r: 0.35, n: 6 }).path
.exploded()
.flatMap((p) => p.exploded())
.flatMap((p) => p.exploded({ scale: 1.2 }))
.forEach((p, i) => {
s.setFillColor(...cols[i % cols.length])
s.fill(p)
})
2
This one uses some HTML canvas capabilities, which are nicely exposed by Solandra. We can fill a linear gradient with arbitrary clipping (which, with Solandra, we can do via a function passed to withClipping
).
s.background(210, 70, 10)
s.withClipping(new Circle({ at: s.meta.center, r: 0.4 }), () => {
s.backgroundGradient(
new LinearGradient({
from: [0.5, 0],
to: [0.5, s.meta.bottom],
colors: [
[0, { h: 210, s: 80, l: 40 }],
[1, { h: 190, s: 60, l: 60 }],
],
}),
)
s.forPoissonDiskPoints({ minDist: 0.05 }, (pt, i) => {
if (perlin2(...v.scale(pt, 4)) > 0.05) {
s.setFillColor(210, 10, 90, 0.4)
s.fill(new RegularPolygon({ at: pt, r: 0.06, n: 6, a: s.randomAngle() }))
}
})
})
3
Here, we use the new forPoissonDiskPoints
function to iterate over a bunch of points created by the Poisson disk algorithm.
We can colour nicely based on a palette using the perlin2
function to get a nice, natural-looking value.
const cols = palettePreset('autumnal', 20)
const c = (v: number) => cols[Math.floor(((1 + v) * cols.length) / 2)]
s.background(40, 70, 80)
s.forPoissonDiskPoints({ minDist: 0.05 }, (pt, i) => {
const value = perlin2(...pt)
s.setFillColor(...c(value))
s.fill(new Circle({ at: pt, r: 0.02 }))
})
4
This is one of the longer examples, but it shows how we can layer several arbitrary drawings to create something interesting. It was adapted from one of the many examples in the Solandra docs.
const { bottom, right } = s.meta
s.backgroundGradient(
new LinearGradient({
from: [0, 0],
to: [0, bottom],
colors: [
[0, { h: 215, s: 80, l: 60 }],
[1, { h: 215, s: 80, l: 20 }],
],
}),
)
s.times(40, () => {
s.setFillColor(s.uniformRandomInt({ from: 180, to: 225 }), 80, 40, 0.3)
s.fill(
new Rect({
at: [0, s.gaussian({ mean: bottom * 0.6, sd: bottom * 0.15 })],
w: right,
h: bottom * 0.1,
}),
)
})
s.lineWidth = 0.005
s.times(20, () => {
s.setStrokeColor(0, 0, 100, 0.2)
const y = s.gaussian({ mean: bottom * 0.8, sd: bottom * 0.1 })
s.draw(new Line([0, y], [right, y]))
})
5
One of Solandra's nicest features is an original take on drawing curves, with an approach that, unlike standard Bezier curves:
- allows for the shape to remain the same as the start and end points change
- has a more natural, human set of parameters: you set the 'curve size' or 'bulbousness' rather than control points.
Here, we draw a bunch of curves between points with a variety of pseudo-random parameters.
s.backgroundFromSpec({ h: 30, s: 50, l: 90 })
const hr = palettePreset('rusty', 22)
const {
bottom: b,
right: r,
center: [cX, cY],
} = s.meta
const u: Point2D = [cX - r / 3.5, cY - b / 3.5]
const v: Point2D = [cX + r / 3.5, cY + b / 3.5]
s.lineWidth = 0.02
s.range({ from: -10, to: 10, n: 20 }, (n) => {
s.setStrokeColor(...hr[(n + 10) % 20])
s.draw(
Path.startAt(u).addCurveTo(v, {
curveSize: n / 10,
curveAngle: s.gaussian(),
bulbousness: 1 + Math.cos(s.t / 2 + n),
}),
)
})