Canvas Basics

A sketch is a function that takes an SCanvas and draws with it. The examples call this argument s or p:

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 }))
}

The coordinate system

The canvas width is always 1, regardless of its pixel size. Its height is 1 / aspectRatio, so the same sketch can render at different resolutions.

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 / height

Points are [x, y] tuples. The origin is at the top left; x increases to the right and y increases downwards.

This example marks the corners, edges and centre:

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 }))
})

Drawing and filling

Pass a shape to s.draw or s.fill. Both accept anything that implements Traceable; see Shapes.

  • s.draw(shape) strokes the outline with the current stroke colour and line width
  • s.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

Colours use HSLA: hue (0–360), saturation (0–100), lightness (0–100) and optional alpha (0–1). See Colour for gradients, palettes and mixing.

s.background(45, 20, 95) // hue, saturation, lightness
s.setFillColor(220, 70, 50, 0.5) // ...and optional alpha
s.setStrokeColor(0, 0, 20)

The FromSpec variants take a { h, s, l, a } object:

const brand = { h: 175, s: 80, l: 40 }
s.setFillColorFromSpec(brand)
s.backgroundFromSpec({ h: 45, s: 20, l: 95 })

Line style

Line width uses canvas units: 0.01 is 1% of the canvas width. You can also set 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]))

Dash lengths use canvas units, not pixels.

Shadows

Set a shadow with shadow and remove it with clearShadow. Blur and offsets use canvas units:

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 }))

Checking whether a point is visible

s.inDrawing(point) checks whether a point is inside the canvas. Use it to stop a random walk at the edge:

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 }))
})

Where next

  • Shapes for everything you can pass to draw and fill
  • Iteration for grids, tiling and loops
  • Randomness for the seeded random number APIs used above

Solandra was made by James Porter.

Check out the GitHub page or install with npm i solandra