Class: PencilSurface
Defined in: surface.ts:90
Constructors
Constructor
new PencilSurface(
opts):PencilSurface
Defined in: surface.ts:124
Parameters
opts
Returns
PencilSurface
Properties
canvas
readonlycanvas:HTMLCanvasElement
Defined in: surface.ts:91
Methods
clear()
clear(
auto?):void
Defined in: surface.ts:295
Parameters
auto?
boolean = false
Returns
void
clearAnimated()
clearAnimated(
durationSec?):void
Defined in: surface.ts:369
Clear, but let the ink go the way vanishing ink goes: every stroke jumps to the charge-and-pop tail of the fade curve — it thickens, heats, and pops, over durationSec — and then the surface is really cleared. The same curve a fading stroke rides; an animated clear is just all of them riding it at once.
This is also D4's retire mechanism: an overlay whose viewport scrolled or reflowed calls this, and the disappearance reads as intentional rather than as a glitch. Strokes already flattened past the horizon have no dabs left to warp, so settled alpha-fades as a layer on the same clock — an honest degradation, and one the overlay never sees (with fade active nothing ever reaches settled).
Parameters
durationSec?
number = 0.5
Returns
void
dispose()
dispose():
void
Defined in: surface.ts:434
Returns
void
hasInk()
hasInk():
boolean
Defined in: surface.ts:194
Returns
boolean
ink()
ink():
InkState
Defined in: surface.ts:250
The drawing, as data: committed strokes (including those flattened past the undo horizon — still part of the picture) plus the strokes in flight with every point captured so far. Fresh arrays each call, so reference equality means "actually changed"; committed point arrays are shared (they never mutate), live ones are copied (they do).
Returns
inkBounds()
inkBounds():
Rect|undefined
Defined in: surface.ts:203
Returns
Rect | undefined
params()
params():
PencilParams
Defined in: surface.ts:275
The instrument as currently configured — so a downstream consumer can run the widget's own pipeline (planStroke(stroke.points, surface.params())) or substitute parameters of its own. The math itself is exported from the package root: planStroke, densify, detectCusps, PointFilter.
Returns
popCompleted()
popCompleted():
void
Defined in: surface.ts:327
Send every COMPLETED stroke into the TAIL of the vanishing curve — the charge-and-pop — so previous marks animate OUT rather than snapping off, while leaving the stroke still under the pen untouched.
This is the "one mark at a time" primitive. PencilSurfaceOptions.onStrokeStart fires from inside beginStroke, after the new stroke has joined the live set, so — like a plain clearAnimated — touching live here would wipe the mark just begun; this re-times only the completed strokes. Each is advanced to the hold→pop boundary of the AMBIENT fade window (fadeSec) and left to pop on the normal per-frame fade, so starting a new stroke triggers the previous one's dissolve exactly as if its clock had run out. A stroke already in its tail is not re-charged (min keeps the earlier clock). With no fade window (fadeSec 0) there is nothing to animate against, so the completed strokes are dropped at once.
Returns
void
remoteBegin()
remoteBegin(
id,init):void
Defined in: surface.ts:404
Parameters
id
string
init
params
point
tool
Returns
void
remoteCancel()
remoteCancel(
id):void
Defined in: surface.ts:428
Parameters
id
string
Returns
void
remoteEnd()
remoteEnd(
id,point?):void
Defined in: surface.ts:417
Parameters
id
string
point?
Returns
void
remotePoint()
remotePoint(
id,point):void
Defined in: surface.ts:408
Parameters
id
string
point
Returns
void
repaint()
repaint():
void
Defined in: surface.ts:228
Force the next frame to redraw, even though nothing changed.
Exists for one consumer: a captureStream() of this canvas. The stream emits a frame only when the canvas REPAINTS, and this surface deliberately paints nothing when still ("free battery on an iPad") — so a viewer joining a quiet host would stare at a stream that has never produced a frame. The host keeps the stream warm by calling this on a slow tick while anyone is watching; the cost is a few drawImage calls per tick.
Returns
void
restartFade()
restartFade():
void
Defined in: surface.ts:394
Restart every stroke's fade clock. Turning vanishing ink ON is why this exists: bornAt is pen-up time, so ink that sat on a permanent canvas for minutes is already older than any fade window, and flipping the switch would blink the whole drawing out in a single frame.
Returns
void
setActive()
setActive(
on):void
Defined in: surface.ts:177
Parameters
on
boolean
Returns
void
size()
size():
object
Defined in: surface.ts:190
Returns
object
height
height:
number
width
width:
number
strokeCount()
strokeCount():
number
Defined in: surface.ts:199
Completed strokes still individually addressable (i.e. still undoable).
Returns
number
subscribe()
subscribe(
listener): () =>void
Defined in: surface.ts:238
Watch the drawing change. "strokes" fires at human rate (commit, undo, clear, fade-out); "live" fires per pointer batch — 60–120 Hz while the pen moves. The events carry nothing: read ink when you care, so a throttled subscriber pays for snapshots at ITS rate, not the pen's.
Parameters
listener
(event) => void
Returns
() => void
translate()
translate(
dx,dy):void
Defined in: surface.ts:736
Translate every stroke by (dx, dy) CSS px — the document-anchored overlay's REBASE (owner, 2026-07-17). Stroke tiles are bounds-local, so ink translated past the canvas edge keeps its points AND its pixels and re-bakes back into view on a later translate: the overlay's window slides over an unbounded drawing, viewport-sized memory for the visible raster. settled (raster past the points horizon) cannot move and is dropped — rescale's same honest degradation; fade-active surfaces never grow one. Fade clocks are PRESERVED (a rebase is bookkeeping, not new ink).
Parameters
dx
number
dy
number
Returns
void
undo()
undo():
boolean
Defined in: surface.ts:285
Undo the most recent retained stroke. Returns false when there is nothing left to undo — because the horizon has been crossed and the stroke is now part of settled, which is exactly what flattening MEANS. Undo depth and the retention horizon are the same number by construction, not by coincidence.
Returns
boolean