Three scripts: drawn matter, language operations, typewriter voice. Each keeps its own sheet of live works.

01 · COREp5.gysin.jsCapture, disturb, export. 02 · ADDONp5.gysin.text.jsSentences change order. 03 · ADDONp5.gysin.underwood.jsThe typewriter voice.

p5.gysin.js

A word can stand alone as a drawn body. The core also carries shapes, fields, and finished traces to paper.

WordIsolate it. Enlarge it. Wear it.
TraceParameters, contours, paths.
InkAdditive return passages.
PageMillimetres, pens, layers, export.
SurfaceA late disturbance, used sparingly.

First Trace

Start with clean vector forms and let p5.gysin turn them into uncertain plotter traces.

const plot = new GysinPlot({ seed: 1960 });
const outlineFont = await loadFont(FONT_URL);   // real letterforms

// the same word, three states of the same trace
plot.text("FIRST TRACE", 68, 90, {
  font: outlineFont,
  size: 54,
  breathe: 0.4
});
plot.text("FIRST TRACE", 68, 180, {
  font: outlineFont,
  size: 54,
  breathe: 1.4,
  dropout: 0.08
});
plot.text("FIRST TRACE", 68, 270, {
  font: outlineFont,
  size: 54,
  breathe: 2.2,
  dropout: 0.2,
  rubout: 0.2
});

Horizontal slices

Shift a few contour bands after the word is already working. This is a graphical disturbance, not the historical cut-up procedure.

// use once, as a surface shift
const outlineFont = await loadFont(FONT_URL);
plot.textCutup("SURFACE", 76, 220, {
  font: outlineFont,
  size: 84,
  slices: 5,
  sliceOffset: 12
});

Writing escapes the word

WORN, in three passages: intact, cut, buried.

const plot = new GysinPlot({ seed: 1960 });
const outlineFont = await loadFont(FONT_URL);   // fills the legible head

// one word: legible → cut up → asemic
plot.rub("WORN", 46, 72, {
  size: 48,
  font: outlineFont
});

plot.draw();

2D Primitives

Five clean geometry methods, isolated and named. No disturbance or export option is needed.

const plot = new GysinPlot({ seed: 1960 });

plot.line(42, 180, 126, 180);
plot.rect(176, 134, 92, 92);
plot.circle(360, 180, 92);
plot.polygon([[498, 126], [552, 180], [498, 234], [444, 180]]);
plot.path([[588, 216], [612, 144], [636, 216], [660, 144], [684, 216]]);

plot.draw();

Parameter Lab

Four controls, four elements: breathe, rubout, repeat, dropout.

// one slider owns one element, each in its own ink
plot.text("RUB OUT", 42, 90, {
  size: 44,
  breathe: 1.4,
  stroke: "#b26a00"
});
plot.text("ERASE", 42, 160, {
  size: 30,
  rubout: 0.12,
  stroke: "#b0203a"
});
plot.path(wave, {   // wave: a sampled sine row
  repeat: 2,
  drift: 2.4,
  stroke: "#2b4fb0"
});
for (let i = 0; i < 6; i++) {   // a ruled block, no fill
  plot.line(42, 240 + i * 9, 380, 240 + i * 9, {
    dropout: 0.16,
    stroke: "#0f7a6c"
  });
}

Font Outlines

Use a real p5 font and preserve its outer contours and inner counters as plotter paths.

const outlineFont = await loadFont(FONT_URL);

plot.text("O8BR", 70, 190, {
  font: outlineFont,
  size: 150,
  breathe: 0.9,
  repeat: 2
});

Frequencies

Treat paths as fragile signals: no typography, only a scored machine reading made from vector paths and markers.

const signal = [];
for (let x = 0; x <= 620; x += 8) {
  signal.push([x, y + sin(x * 0.04) * 28]);
}

// row after row of the same signal, each a step more worn
plot.path(signal, {
  dropout: 0.05 + row * 0.01,
  repeat: 2,
  rubout: 0.06 + row * 0.03
});

Ink Bleed

Ink gathers in shifted return passages. Blade export keeps only the first.

// the ink returns harder on each line
plot.text("I AM THAT I AM", 42, 90, { font: outlineFont, size: 34 });
plot.text("I AM THAT I AM", 42, 180, {
  font: outlineFont,
  size: 34,
  bleed: 0.22,
  bleedPasses: 2
});
plot.text("I AM THAT I AM", 42, 270, {
  font: outlineFont,
  size: 34,
  bleed: 0.4,
  bleedPasses: 3,
  bleedSpread: 1.2
});

Plotter Calibration

A practical calibration sheet for margins, circles, dropout, overshoot, multi-pen output, and route statistics.

// Advanced: this canvas is a millimetre calibration grid.
const page = { width: 210, height: 297, units: "mm", margin: 10, scale: 1 / 3, clip: true };

plot.downloadHPGL("calibration.hpgl", {
  page,
  penMap: { frame: 1, circle: 2, hatch: 3 },
  speed: 20
});

Plotter Export

One 720 × 900 poster becomes a physical two-pen SVG without rewriting its composition.

loads p5.gysin.min.js + p5.gysin.underwood.min.js

// SVG groups become physical pen passes.
const penMap = { black: 1, red: 2 };

// "A4" is the built-in physical page preset.
plot.downloadPlotterSVG("poster.svg", {
  page: "A4",
  penMap
});

p5.gysin.text.js

A sentence stays readable while its order or source changes. permute() turns one phrase; weave() brings separate sources together without losing their provenance.

SentenceOrder, sequence, source.

Coincidence is a method

The same words return in changing positions. Their order carries the movement.

loads p5.gysin.min.js + p5.gysin.text.min.js

const plot = new GysinPlot({ seed: 1960 });

plot.chant("I LOVE YOU", width * 0.075, height * 0.12, {
  lines: 6,
  size: width * 0.11,
  leading: height * 0.14,
  font: outlineFont
});

plot.draw();

At the seam

Fragments from separate texts cross. The JSON keeps the address of every borrowed piece.

loads p5.gysin.min.js + p5.gysin.text.min.js

const plot = new GysinPlot({ seed: 1960 });

plot.weave(sources, width * 0.055, height * 0.145, {
  lines: 4,
  unit: "clause",
  fragments: 2,
  maxWidth: width * 0.89
});

plot.draw();

Rotations

The sheet turns between passages. A grid persists; orders cross it at their own angles.

loads p5.gysin.min.js + p5.gysin.text.min.js

// write the field, turn the sheet, write across - then the script over it
plot.lattice("COME TO FREE THE WORDS", -15, -15, 730, 1190, { turns: [4, 94] });
GysinText.permute("COME TO FREE THE WORDS", { order: "rotate" })
  .forEach((row, i) => {
    plot.text(row, x[i], y[i], { size: 24, angle: a[i] });
  });

p5.gysin.underwood.js

Longer voice gets fixed pitch, line height, margins, and a page. The machine keeps only two decorations: an underscore rule and the same key struck twice.

TypewriterFixed pitch, line height, double strike.

Typewriter

Fixed-pitch single-stroke letters. The underscore is a rule; emphasis is a second strike.

loads p5.gysin.min.js + p5.gysin.text.min.js + p5.gysin.underwood.min.js

// defaults alone = a period-correct typed page
plot.underwood("KICK THAT HABIT MAN", 60, 90);

// the only decoration the machine had:
plot.underwood("HEADING", 60, 130, { underline: 1 });
plot.underwood("BOLD?", 60, 160, { bold: true });   // struck twice

// both cases live on the same keys:
plot.underwood("the small voice.", 60, 190);

The Letter

A complete typed letter. One word is struck through.

loads p5.gysin.min.js + p5.gysin.underwood.min.js

plot.underwood("DEAR READER,", 60, 90, { underline: 1 });
plot.underwood("FORGET", 60, 150);
plot.underwood("XXXXXX", 60, 150, { bold: true });   // struck over
plot.underwood("the small hand.", 60, 210);

Not pixels. Paths.

The same seeded traces pass through canvas and onto paper. Methods, addressing and export live in System.