Everything in Solandra happens through an SCanvas (by convention called s or p). Your sketch is just a function that takes an SCanvas and draws with it:
import { SCanvas } from "solandra"
const sketch = (s: SCanvas) => {
s.background(45, 30, 95)
s.setFillColor(220, 70, 40)
s.fill(new Circle({ at: s.meta.center, r: 0.3 }))
}Solandra uses a normalized coordinate system: the width of the canvas is always 1, whatever the actual pixel size. The height is 1 / aspectRatio. This means your sketches are resolution independent by default: the same code renders a thumbnail or a poster.
The meta property tells you about the drawing area:
s.meta.top // 0
s.meta.left // 0
s.meta.right // 1
s.meta.bottom // 1 / aspectRatio (1 for a square canvas)
s.meta.center // [0.5, 0.5 / aspectRatio]
s.meta.aspectRatio // width / heightPoints are just tuples of numbers, [x, y], with x increasing rightwards and y increasing downwards (as is standard for canvases, if not mathematics).
A quick tour of the drawing area; here we mark the corners, edges and center:
const { center, bottom, right } = s.meta
s.background(210, 30, 20)
s.lineWidth = 0.005
s.setStrokeColor(0, 0, 90)
s.draw(new Line([0, 0], [right, bottom]))
s.draw(new Line([right, 0], [0, bottom]))
s.setFillColor(20, 90, 60)
s.fill(new Circle({ at: center, r: 0.05 }))
s.setFillColor(50, 90, 60)
const corners = [
[0, 0],
[right, 0],
[right, bottom],
[0, bottom],
]
corners.forEach((at) => {
s.fill(new Circle({ at, r: 0.03 }))
})Solandra separates what you draw from how you draw it. Shapes (anything implementing Traceable, see Shapes) are passed to one of two methods:
s.draw(shape) strokes the outline with the current stroke colour and line widths.fill(shape) fills the interior with the current fill colour (or gradient)s.background(0, 0, 95)
s.setStrokeColor(220, 80, 30)
s.lineWidth = 0.02
s.draw(new Rect({ at: [0.1, 0.1], w: 0.35, h: 0.35 }))
s.setFillColor(340, 80, 60)
s.fill(new Rect({ at: [0.55, 0.55], w: 0.35, h: 0.35 }))Colours in Solandra are HSLA: hue (0–360), saturation (0–100), lightness (0–100) and optional alpha (0–1). This makes it easy to programmatically vary colours in ways that look good; see Colour for much more.
s.background(45, 20, 95) // hue, saturation, lightness
s.setFillColor(220, 70, 50, 0.5) // ...and optional alpha
s.setStrokeColor(0, 0, 20)The state-based colour setters have FromSpec variants that take a { h, s, l, a } object, which is handy when passing colours around:
const brand = { h: 175, s: 80, l: 40 }
s.setFillColorFromSpec(brand)
s.backgroundFromSpec({ h: 45, s: 20, l: 95 })Line width is also in canvas units (so 0.01 is 1% of the width of your canvas). You can also control caps, joins and dashes:
s.background(0, 0, 15)
s.setStrokeColor(45, 90, 70)
s.lineWidth = 0.02
s.lineStyle = { cap: "round" }
s.draw(new Line([0.1, 0.2], [0.9, 0.2]))
s.lineStyle = { cap: "butt" }
s.draw(new Line([0.1, 0.4], [0.9, 0.4]))
s.dash = { pattern: [0.04, 0.02] }
s.draw(new Line([0.1, 0.6], [0.9, 0.6]))
s.dash = { pattern: [0.005, 0.02] }
s.lineStyle = { cap: "round" }
s.draw(new Line([0.1, 0.8], [0.9, 0.8]))The dash pattern is specified at the scale of the canvas (unlike raw HTML5 Canvas, where it would be in pixels).
Shadows are configured with a single setter (again scaled to canvas size, unlike the raw Canvas API) and removed with clearShadow:
s.background(210, 20, 95)
s.shadow = {
size: 0.03,
dX: 0.01,
dY: 0.02,
color: { h: 210, s: 40, l: 30, a: 0.6 },
}
s.setFillColor(340, 70, 55)
s.fill(new Star({ at: [0.35, 0.4], r: 0.2, n: 5 }))
s.clearShadow()
s.setFillColor(220, 70, 55)
s.fill(new Circle({ at: [0.7, 0.65], r: 0.15 }))s.inDrawing(point) returns whether a point lies inside the canvas; useful for terminating random walks or culling offscreen work:
s.background(0, 0, 10)
s.lineWidth = 0.005
s.times(20, (n) => {
s.setStrokeColor(180 + n * 6, 80, 60, 0.8)
const path = SimplePath.startAt(s.meta.center)
let pt = s.meta.center
while (s.inDrawing(pt)) {
pt = s.perturb({ at: pt, magnitude: 0.1 })
path.addPoint(pt)
}
s.draw(path.chaiken({ n: 2 }))
})draw and fillSolandra was made by James Porter.
Check out the GitHub page or install with npm i solandra