The line

Up to here a face is a list of point sequences. What turns that into a drawing is three decisions in the pen – and all three depend on every frame being drawn anew.

macheStift gets a drawing surface, a seed, an ink colour, a scale factor and a beat. Back come three tools: zug, flaeche, punkt. The whole style needs no more. All measures are unit measures – the caller has long since scaled the surface to head size, so a width of 0.024 is a good two percent of the head height.

Every line is a surface

No ctx.stroke(). A stroke is a ribbon: the centre line plus half a width to each side, as a single filled polygon. The reason is the width itself – it is supposed to change within a stroke: thin on touching down, full in the middle, thin on lifting off.

A line in canvas has a width, and the same one everywhere. Whoever wants the taper anyway needs one stroke() per segment. At nine heads times forty strokes times sixty frames a second, that is the difference between smooth and sluggish.

The pressure curve is spitz: 0 means blunt ends, 1 means running out to a point, the default is 0.75. The sine term is deliberately kept flat – a line twice as thick in the middle as at the ends looks like a brush, not like a pen.

The ribbon is also the reason flaeche() exists as a tool of its own and not just as a closed stroke: a surface has no width, it has an edge. What happens to it in printing is in the chapter The press.

The warp has a name

A hand-drawn line does not hit its intended curve. The obvious implementation: a random stream from which every stroke draws its deviation. It is wrong, and not by a little.

The flickering face

Symptom: During a blink it is not the eye that changes shape but half the face – nose, mouth and cheeks jump to a different drawing for three frames.

Cause: The warp hung off the call order. A closed lid is one stroke instead of four; everything drawn afterwards draws its random numbers offset by four places and is therefore a different line.

Fix: The warp hangs off a TRACE – a name: "kontur", "auge-l". The same name plus the same seed gives the same tremor in every frame, no matter what was drawn before it or not drawn at all.

From the name an FNV hash makes a start value, and from the start value a field of 52 support values. It lives in a Map on the pen: the same trace is needed several times per frame, and a head does not have a hundred of them.

The line boils

A hand drawing that moves is animation: every frame is a drawing of its own, and none of them hits the previous one exactly. So every trace gets a BEAT as well – TAKT_JE_SEKUNDE = 8. Within one beat everything stands still, between two beats every line slips a little.

The beat does not come from the pen but from above – the style computes it when drawing each head: takt = Math.floor(zeit * TAKT_JE_SEKUNDE + d.taktVersatz).

The taktVersatz is rolled per head. Without it nine heads restart at the same moment, and a sheet twitching in lockstep looks like a display fault.

Inside the pen two streams are mixed for this: one hangs only off the seed – the hand – and one off the beat as well – the frame. The mix is 0.62 to 0.5. With only the frame stream the line jumps somewhere else at every beat; with only the hand it stands still.

Rolled anew sixty times a second would be noise, eight times is a pen. The slider shows both.

koch hangs off the length

Not every stroke may boil equally. A mouth that changes length and arc eight times a second is no longer a pen but restlessness. So zug() first measures the length of the path:

Lengthkochwho
below 0.40 – the hand onlymouth, brow, nose, spike
0.4 to 1.2mixedlid arcs, ears
from 1.21 – fully on the beatcontour, hair mass

The same length also controls the strength of the warp (kurz, capped at half the amplitude): the warp is a measure in head space, and the same amount is a meander on a long contour but a jump of the whole line on a short brow.

Beside that there is ruhig as a hard switch. Eye rings, hatching and forehead lines are pale and fine – their boiling would not read as a tremor of the hand but as a flicker of the picture.

The second layer

Under every stroke lies the same stroke once more: half as wide, at 32 % opacity – and with a warp of its own (trace name~, amplitude 1.7 times plus 0.004). That is precisely where the pencil lives: the second layer does not lie on the first, but beside it.

einlagig: true switches it off. That applies to the edges of colour surfaces and to trimmings, where the doubling would only be mush – the pale lower lip in the grin, for instance, is a hair's breadth wide, and a second layer beside it turns it into a stain.

From point sequence to path

Before a ribbon comes about at all, the point sequence becomes an evenly sampled path: entdoppeln, then catmull, then sampling by arc length. A mouth made of three points has to become an arc, not a kink.

The stroke that flickers by one step

Symptom: Short lines – brows, mouth corners – tremble at their ends, although the warp hangs off a fixed trace and ought not to boil at all.

Cause: The sum of steps hits the end point only up to rounding. Whether the last step just fits or just does not tips with every tiny change in length – and the stroke is up to one step longer or shorter from frame to frame.

Fix: The end point belongs always. If the last point set lies further than half a step away, the end point is appended; otherwise it replaces it.

Closed strokes run 3.5 % past their start. The place where the hand set down stays visible – a circle that runs back into itself exactly is an ellipse from a drawing program.

Meander and scratch

The warp itself comes from two waves: a coarse one with 12 support values (the meander) and a fine one with 40, weighted at 0.35 (the scratch). Both are interpolated cubically.

The meander alone would look like a rubber hose, the scratch alone like a short circuit.

A dot is not a circle

A filled circle is the place where a drawing looks like vector graphics fastest: ctx.arc hits the curve exactly, and that is exactly what the eye recognises – with a pupil, a freckle and a stubble dot alike.

So punkt() goes the same way as any other stroke: a slightly oval, tilted ring, warped with this pen's noise, and beneath it a pale seam from the bleed. Four details decide:

Detailwithout it
a separate spur per dot two pupils with the same bulge in the same place – twins
edge not computed as closed the noise runs from 0 to 1 and at the end does not meet the start; that is where the set-down mark sits
warp capped at the radius (0.15 r, at most 0.007) the freckle becomes an amoeba
ohneSaum for dots lying ON something the highlight in the eye gets a pale rim and looks like a stain

A dot does not boil. It always draws its trace calmly, without a beat. A pupil that sits somewhere else at every beat is a tremor, not a line.

The feld is the last ingredient: the four values a field carries anyway (ex, ey, fx, fy, see Space). Without them the dot draws in screen space – the lid arcs of a turned head squash, the pupil inside them stays a full circle and juts out past the contour at the side. The field is left out for everything that is not on the head: an antenna ball stands in space, no cheek carries it.

A hand draws large with the same pen

A head in the close-up is three times as large as in the grid. If the line simply grew with it, it would be three times as thick there – a felt-tip. But it must not stay exactly the same either: the same pixel line that is right when large is a hair in the grid. The comment below says how the factor mediates between the two.

Exponent 0.4, clamped to 0.62 up to 1.3, reference size 95 – the size a head has in the grid. In the grid the factor is therefore 1; in the close-up it sinks towards the clamp.

The factor is set once when the pen is created (strichFaktor(at.mass)) and multiplies every width after that. No drawing step has to know about it.