Canvas API
The drawing commands, inputs, and interaction regions of a paint.ts canvas.A canvas is declared in ui.ts and drawn in canvases/<id>/paint.ts. The paint
module runs on the UI thread (its own wasm), once per frame. For the how-to, see
Draw your own canvas.
import { Paint, definePaint } from "@msh/device-sdk/device/assembly/paint";
definePaint((p: Paint): void => {
// draw here, runs once per frame
});
export {
mshPaint,
paintCmdPtr,
paintInputPtr,
paintInputCapacity,
paintStrPtr,
} from "@msh/device-sdk/device/assembly/paint";Coordinates are logical pixels in [0, p.width] by [0, p.height]. Colours are
four 0..1 floats. Always re-export the paint* ABI line at the bottom.
For a GPU-drawn canvas, declare type: "webgpu" and write a fragment shader
instead. See GPU shader canvas.
Reading inputs
Inputs come from the Canvas({ inputs }) list, read by position. Each input is a
param scalar, a colour token, a channel, a modulation("id"), or a curve("id").
| Call | Reads |
|---|---|
p.scalar(i) | A scalar input (a param, modulated value, or setting), normalized. |
p.color(i, ch) | A colour input; ch 0 to 3 is r, g, b, a (0 to 1). |
p.channelLen(i) | Bin count of a channel input. |
p.channel(i, bin) | A bin of a channel input. |
p.input(idx) | Raw f32 at a flat word index (escape hatch). |
Modulation state
Feed a param's full live modulation state with modulation("id") to draw your own
dial with a mod arc:
| Call | Reads |
|---|---|
p.modBase(i) | The knob's set value, normalized. |
p.modValue(i) | The live post-modulation value, animates each frame. |
p.modLow(i) / p.modHigh(i) | Combined modulation range edges. |
p.modActive(i) | Whether the param has any active source. |
p.modSourceCount(i) | Number of modulation sources. |
p.modSourceLow(i, k) / p.modSourceHigh(i, k) | Source k's range edges. |
p.modSourceMuted(i, k) | Whether source k is muted. |
p.modSourceColor(i, k, ch) | Source k's accent colour (ch 0 to 2 = r, g, b). |
p.drawModArc(i, cx, cy, r, lineWidth) | Draws Msh's standard value + range arc and live dot for you. |
Curve points
A curve("id") input exposes the editable curve's control points:
| Call | Reads |
|---|---|
p.curve(i, x) | Sample the piecewise curve at x in [0, 1], returns y in [0, 1]. |
p.curvePointCount(i) | Number of control points. |
p.curvePointX(i, k) / p.curvePointY(i, k) | Point k's position. |
p.curvePointPower(i, k) | Segment power (bias) starting at point k. |
p.curvePointCurved(i, k) | Whether the segment is curved or forced linear. |
Drawing
A Canvas2D-style API on p. Out-of-capacity ops are dropped, never out of bounds.
- Style:
fillStyle(r,g,b,a),strokeStyle(r,g,b,a),lineWidth,globalAlpha,lineCap,lineJoin,miterLimit,setLineDash,clearLineDash,lineDashOffset. - Shapes:
fillRect,strokeRect,clearRect. - Paths:
beginPath,moveTo,lineTo,arc,arcTo,ellipse,rect,roundRect,quadraticCurveTo,bezierCurveTo,closePath,fill,stroke,clip. Passtruetofill/clipfor the even-odd rule (holes). - Transforms:
save,restore,translate,scale,rotate,transform(a..f). - Gradients:
createLinearGradient(id, ...),createRadialGradient(id, ...),createConicGradient(id, startAngle, x, y), thengradientColorStop(id, t, r,g,b,a)per stop, thenfillGradient(id)/strokeGradient(id). - Text:
font(css),textAlign,textBaseline,letterSpacing,wordSpacing,direction,fontKerning,fillText(text, x, y, maxWidth?),strokeText. - Effects:
shadow(dx, dy, blur, r, g, b, a)/clearShadow(),blur(px),globalCompositeOperation(CompositeOp.X). An inner shadow is a drop shadow clipped to the shape. - Images: declare with
Canvas({ images: [...] }), draw withp.drawImage(index, dx, dy, dw, dh)orp.drawImageRect(index, sx,sy,sw,sh, dx,dy,dw,dh)for one cell of a sprite sheet. Togglep.imageSmoothing(false)for crisp pixels.
Interaction
Interaction is declared next to the Canvas in ui.ts, not drawn. Regions use
canvas fractions (0 to 1), so they track the art as the canvas resizes.
| Region | Behaves like |
|---|---|
hits: [{ bind, x, y, w, h, axis, fine }] | A hidden knob. Dragging edits the bound param. axis is "vertical" (default) or "horizontal"; fine (0 to 1) tunes Shift-drag slowdown. |
clicks: [{ bind, x, y, w, h, action, value, step }] | A button on a param. action: "toggle" (default), "set" (to value), or "cycle" (by step, wrapping). |
commands: [{ command, x, y, w, h, value }] | Dispatches command to Device.onCommand with the hit position. See Control surface. |
Msh provides the modulation wheel tray and right-click settings on hits and
clicks. You choose whether to draw the dial arc (p.drawModArc does it to spec).
Canvas({
id: "viz",
width: "fill",
height: Theme.sizing(20),
inputs: [param("amount"), channel("spectrum"), Theme.colors.accent],
hits: [
{ bind: param("amount"), x: 0.3, y: 0, w: 0.4, h: 1, axis: "vertical" },
],
});