ARC PUDDING — Scripting API

a scriptable compass & rule drawing tool

Overview

Top-level code is setup (runs once). Return a function to create an update loop (runs every frame).

t is elapsed time in seconds since the script started.

const c = compass(150)
c.pen('#ff0000')

return t => {
  c.radius = 100 + 50 * sin(t)
}

Factories

CallReturnsDescription
compass(radius?)CompassNodeCircle tool at origin (default radius 100)
ruler(angle?)RulerNodeLine tool at origin (default angle 0)

CompassNode

PropertyDescription
.x, .yPosition (world coords)
.radiusCircle radius
.angleRotation of the compass (rotates all pens and attached children)
.speedAuto-advance angle each frame (angle = t * speed). Default 0
.offsetAttachment offset on parent (angle in radians for compass, distance in pixels for ruler)
.draggableAllow user to drag the compass (default false)
.resizableAllow user to resize the compass (default false)
.rotatableAllow user to rotate the compass (default false)
.showGuidesShow/hide guide circles (default true)
.on(parent, offset?)Attach to parent node at given offset (default 0). Returns this
.off()Detach. Returns this
.pen(color?, size?, offset?)Create pen on edge at offset (radians). Auto-activates. Returns PenNode

RulerNode

PropertyDescription
.x, .yPosition (world coords)
.angleRuler angle
.speedAuto-advance angle each frame (angle = t * speed). Default 0
.offsetAttachment offset on parent (angle in radians for compass, distance in pixels for ruler)
.draggableAllow user to drag the ruler (default false)
.rotatableAllow user to rotate the ruler (default false)
.showGuidesShow/hide guide line (default true)
.on(parent, offset?)Attach to parent node at given offset (default 0). Returns this
.off()Detach. Returns this
.pen(color?, size?, offset?)Create pen on line at offset (pixels). Auto-activates. Returns PenNode

PenNode

PropertyDescription
.offsetPosition parameter — angle in radians for compass, distance in pixels for ruler
.angleRotation of the pen shape (radians)
.colorPen color (default black)
.sizePen size in pixels (default 1)
.activeWhether pen is drawing (read/write — setting true activates, false deactivates)
.drawOnPressWhen true, pen only draws while mouse is held down (default false)
.shapePen tip shape — 'circle' (default) or a single character from the loaded simplex font
.shapeWidthRead-only. Computed width of the current shape at the current size
.filledWhen true, shape stamps are filled instead of stroked (default false)
.strokeSizeStroke width when drawing shapes (default 1)
.flowMinimum distance in pixels between stamps (default 1)
.flowGapExtra gap between stamps in pixels, or null for default spacing
.shapeSequenceArray of shape strings to cycle through, or null for single shape
.sequenceLimitStop drawing after this many stamps per sequence cycle, or null for unlimited
.sequenceIndexRead-only. Current index within the shape sequence
.sequenceCyclesRead-only. Number of complete sequence cycles so far
.stampCountRead-only. Total number of stamps drawn by this pen
.lastStampWidthRead-only. Width of the most recently drawn stamp
.activate()Start drawing
.deactivate()Stop drawing

Globals

Math (no Math. prefix)

sin, cos, tan, atan2, sqrt, abs, floor, ceil, round, min, max, pow, log, PI, TAU

Helpers

lerp(a, b, t), clamp(v, lo, hi), easeInOut(t), easeIn(t), easeOut(t), degToRad(degrees), radToDeg(radians)

Noise & random

noise(x, y?) — 2D Perlin noise, returns -1 to 1

random(min?, max?) — no args: 0–1, one arg: 0–max, two args: min–max

Color helpers

hsl(h, s?, l?) (defaults: s=100, l=50), rgb(r, g, b), rgba(r, g, b, a)

Color formats

hex '#ff0000', '#0af' — rgb 'rgb(255, 0, 0)' — rgba 'rgba(255, 0, 0, 0.5)' — hsl 'hsl(0, 100%, 50%)' — hsla 'hsla(0, 100%, 50%, 0.5)'

Input

PropertyDescription
mouse.x, mouse.yWorld coords, updated each frame
mouse.pressedBoolean, true while mouse button held
mouse.velocitySmoothed, 0–1
mouse.directionSmoothed angle of movement in radians
mouse.rawVelocityUnsmoothed 0–1
mouse.rawDirectionUnsmoothed angle in radians
mouse.pressureStylus pressure 0–1
mouse.tangentialPressureBarrel pressure -1–1
mouse.tiltX, mouse.tiltYStylus tilt in degrees, -90–90
mouse.twistStylus twist in degrees, 0–359

Examples

One-liner:

compass(100).pen()

Spirograph with speed (no update fn needed):

const a = compass(120)
const b = compass(40).on(a)
a.speed = 2
b.speed = 3
b.pen(hsl(220))

Spirograph:

const a = compass(120)
const b = compass(40).on(a)
b.pen('#0000ff')
a.pen('#ff0000')

return t => {
  a.angle = t * 2
  b.angle = t * 3
}

Nested gears:

const a = compass(120)
const b = compass(40).on(a)
const c = compass(15).on(b)
c.pen('#ff0000')

return t => {
  a.angle = t * 2
  b.angle = t * 7
}

Deep nest with color cycling:

const a = compass(120)
const b = compass(60).on(a)
const c = compass(35).on(b)
const d = compass(20).on(c)
const e = compass(10).on(d)
const p = e.pen()

return t => {
  a.angle = t * 1
  b.angle = t * 3
  c.angle = t * 7
  d.angle = t * 13
  e.angle = t * 21
  p.color = hsl(t * 60)
}

Organic motion with noise:

const c = compass(80)
c.pen()

return t => {
  c.x = 100 * noise(t * 0.5, 0)
  c.y = 100 * noise(0, t * 0.5)
  c.radius = 60 + 30 * noise(t)
}

Mouse follower:

const c = compass(80)
c.pen()

return t => {
  c.x = mouse.x
  c.y = mouse.y
}

Draw on press:

const c = compass(100)
const p = c.pen('#000')
p.drawOnPress = true

return t => { c.angle = t }

Ruler:

const r = ruler(0)
r.pen()

return t => {
  r.angle = sin(t) * 0.5
}

Keyboard Shortcuts

KeyAction
19Toggle pen on/off
hToggle handles
sPause / resume script
u / rUndo / redo
fSwitch to Fill tool
Space + dragPan

Compass & Rule only

KeyAction
Tab / Shift+TabCycle selected node
aArc drawing mode
lLine drawing mode
xDelete selected node
cClear ink