p5.gysin has three library files

Load what you need, in this order - with p5.js first when you draw to a canvas:

<script src="https://cdn.jsdelivr.net/gh/seb-prjcts-be/p5.gysin@v1.2.0/p5.gysin.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/seb-prjcts-be/p5.gysin@v1.2.0/p5.gysin.text.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/seb-prjcts-be/p5.gysin@v1.2.0/p5.gysin.underwood.min.js"></script>

The addons are optional. The pure GysinText operations run without p5.js or GysinPlot; the plot.* methods attach when the core is present.

Basic Use

let plot; function setup() { createCanvas(800, 800); plot = new GysinPlot({ seed: 1960 }); plot.text("FIRST TRACE", 80, 180, { size: 72, breathe: 1.2, dropout: 0.06 }); plot.line(80, 260, 720, 280, { breathe: 1, dropout: 0.05, overshoot: 8 }); plot.draw(); }

Constructor and Instance Mode

new GysinPlot(options) accepts seed, width, height, page, and default human, style, and export objects. In p5 instance mode, create the plot after createCanvas():

const plot = p.createGysinPlot({ seed: 1960 });

The helper links drawing and automatic export dimensions to that p5 instance. Capture methods return a shape id. Lookup and mutation methods return detached snapshots, so use update() instead of modifying data returned by get().

Shape Methods

2D Primitives shows line(), rect(), circle(), polygon(), and path() side by side with their clean defaults.

Fields and Marks

These methods fill a region instead of drawing one isolated shape. They return shape ids or cell rectangles that can be addressed later.

const cells = plot.grid(40, 40, 480, 320, 3, 2); // frame grid; returns cell rects plot.letters("RUB OUT", 60, 60, 200, 120); // a decaying letter field plot.symbols(280, 60, 200, 120, { set: "+#&" }); // typewriter operator texture plot.asemic(60, 220, 160, 120); // a cursive, wordless tangle

Shape, Text, Style, and Export Options

Invalid geometry, duplicate ids, excessive sampling, more than 1,000 repeats, and route optimization above 2,000 traces raise an error before export.

Text

Word: isolate one word or short name and treat it as a drawn body with text() or rub(). Scale, repetition, wear, and position carry the gesture.

Sentence: preserve enough reading time for order, sequence, and source to matter. chant(), weave(), and underwood() therefore default to readable lines rather than automatic slicing.

Surface: textCutup() shifts horizontal bands of an existing letter contour. It is a graphical after-effect, not a linguistic cut-up operation; use it explicitly and sparingly.

When the supplied p5 font exposes its outline path, p5.gysin keeps separate glyph contours and counters. Otherwise it uses font.textToPoints(); without a font, it falls back to a simple vector alphabet.

For p5.js 2.x, load fonts with async setup() and await loadFont(...) before passing the font to plot.text().

Trace Parameters

Ink accumulation

dropout and rubout remove material. bleed is their additive counterpart: it selects seeded, contiguous trace fragments and adds a bounded number of coherently offset passages. It does not repeat the complete glyph or shape.

plot.text("RUB OUT THE WORD", 80, 180, { bleed: 0.22, bleedPasses: 2, bleedSpread: 0.8, bleedCluster: 18 });

Generated bleed traces carry data-role="bleed" and data-pass in SVG. Statistics expose bleedPaths, bleedLength, overdrawRatio, and maxLocalPasses.

Composed Methods

Composed methods combine existing capture methods and return the ids they create. Their options can replace or adjust the built-in composition.

plot.rub(text, x, y, options) draws a word worn away in three copies - legible, weathered, nearly gone - then lets an asemic scribble bury the last one. Its default composition uses text and asemic; a custom stage may still select textCutup explicitly.

plot.rub("RUB OUT", 46, 248); plot.rub("RUB OUT", 46, 248, { decay: 2 }); plot.rub("RUB OUT", 46, 248, { font: outlineFont });

rub() returns the id of every copy and tangle, so each can be frozen, rerolled, updated, or exported like any other shape.

The Turned Sheet

Every shape takes angle (degrees) and pivot. This is the physical gesture of the studio - write, turn the paper, write again - as shape geometry, not as an export afterthought. The turn runs before breathe, dropout, bleed and hatching, so every disturbance lives on the turned line, and update() can turn any standing shape.

plot.text("RUB OUT", 80, 120, { angle: 90 }); // about its pen-down anchor plot.text("RUB OUT", 80, 120, { angle: 45, pivot: "center" }); // about its own middle plot.rect(60, 60, 200, 90, { angle: 30, pivot: { x: 60, y: 60 } }); // about an explicit pin plot.update(id, { angle: 180 });

plot.lattice(text, x, y, w, h, options) writes a phrase in rows that fill a field, then writes across it at each requested turn. Each row cycles the word order by one; later passes can carry more wear.

plot.lattice("RUB OUT THE WORD", 60, 60, 480, 480); // two passes, 0 and 90 plot.lattice("JUNK IS NO GOOD BABY", 60, 60, 480, 480, { turns: [0, 90, 180], size: 16, wear: 1.6 });

Optional Text Operations

p5.gysin.text.min.js provides pure text operations and two matching plot methods.

<script src="https://cdn.jsdelivr.net/gh/seb-prjcts-be/p5.gysin@v1.2.0/p5.gysin.text.min.js"></script> const lines = GysinText.permute("I LOVE YOU", { seed: 1960, limit: 6, order: "walk" }); lines.forEach((line, index) => { plot.text(line, 40, 65 + index * 55, { size: 28 }); });

Orders: walk, random, lexical, and rotate. The source line stays first, duplicate words produce only unique lines, and limit is capped at 1,000.

plot.chant(text, x, y, options) draws the result of permute() as readable text() lines. The changing word order is the composition. It is added to each plot when the addon is loaded.

plot.chant("CUT ARRANGE GLUE", 60, 90); plot.chant("CUT ARRANGE GLUE", 60, 90, { order: "rotate" }); plot.chant("CUT ARRANGE GLUE", 60, 90, { slices: 5 }); // explicit surface effect

Weave

const sources = [ { id: "left", text: "The first source." }, { id: "right", text: "The second source." } ]; const woven = GysinText.weave(sources, { seed: 1960, unit: "phrase", lines: 4, fragments: 3 }); const ids = plot.weave(sources, 60, 90, { seed: 1960, unit: "clause", maxWidth: 560 });

GysinText.weave(sources, options) cuts and recombines fragments from two to eight sources without drawing. A source is a string or { id, text }; each text may contain at most 20,000 characters and source ids must be unique. The method does not fetch files or URLs.

Options are seed (default 1), unit (word, phrase, or clause; default phrase), lines (1–100; default 4), and fragments (2–6; default 3). The result is { seed, unit, lines }. Each output line contains text and a fragments array; every fragment records source, sourceIndex, start, end, and text. Offsets refer to the trimmed input text.

plot.weave(sources, x, y, options) draws the result as text and returns the generated ids. It defaults to the plot seed, size: 26, and leading: 42. Set a positive maxWidth to keep the group inside the available width from x: when one line is too long, every line receives the same smaller type size. It never enlarges a group. Provenance is retained in each shape’s params.weave and in JSON export. SVG and HPGL retain shape ids and paths, not source text metadata.

reroll() changes only the drawn trace. To make a new weave, call weave() again with a new seed. freeze() and thaw() behave as they do for other captured shapes.

Optional Typewriter (underwood)

p5.gysin.underwood.min.js adds plot.underwood(text, x, y, options): single-stroke letters at a fixed pitch of ten characters and six lines per inch. The bundled face includes capitals, lowercase, and marks such as + # &.

plot.underwood("KICK THAT HABIT MAN", 60, 90); plot.underwood("HEADING", 60, 130, { underline: 1 }); plot.underwood("BOLD?", 60, 160, { bold: true }); // struck twice plot.underwood("the small voice.", 60, 190);

bold: true double-strikes each letter, underline draws one to three rules, and wear (0 and up) scales strike variation. Every glyph is a single pen line.

Low-level access is available through the frozen GysinUnderwood object. GysinUnderwood.glyphPolylines(d) parses one bundled glyph path into polylines; GysinUnderwood.render(plot, text, x, y, options) calls the renderer used by plot.underwood(). The glyph dataset is exposed as GysinUnderwood.face.

Addressing

const titleId = plot.text("RUB OUT", 80, 180, { breathe: 2 }); plot.freeze(titleId); plot.update(titleId, { stroke: "#b5362b" }); plot.thaw(titleId); plot.regenerate(titleId); plot.reroll(titleId); // explicit new seeded variation plot.remove(titleId); plot.select(titleId); // id-less calls now target this shape plot.freeze(); // same as plot.freeze(titleId) plot.setSeed(1961); // re-seed and regenerate every unpinned shape plot.clear(); // empty the sheet: shapes, ids, and selection

get(id) returns a detached snapshot. regenerate() recalculates with the same seed; reroll() assigns a new deterministic variation. select() sets the default target for id-less addressing calls; setSeed() changes the plot seed and regenerates every shape without an explicit seed of its own; clear() removes all shapes and the selection.

Public Method Index

Core creation and drawing: new GysinPlot(), p.createGysinPlot(), plot.line(), plot.rect(), plot.circle(), plot.polygon(), plot.path(), plot.text(), plot.textCutup(), plot.asemic(), plot.grid(), plot.letters(), plot.symbols(), plot.rub(), plot.lattice(), and plot.draw().

Core addressing: plot.get(), plot.select(), plot.freeze(), plot.thaw(), plot.regenerate(), plot.reroll(), plot.remove(), plot.update(), plot.setSeed(), and plot.clear().

Core output: plot.exportSVG(), plot.exportPlotterSVG(), plot.exportJSON(), plot.exportHPGL(), plot.downloadSVG(), plot.downloadPlotterSVG(), plot.downloadJSON(), plot.downloadHPGL(), and plot.stats().

Text addon: GysinText.permute(), GysinText.weave(), plot.chant(), and plot.weave(). Typewriter addon: plot.underwood(), GysinUnderwood.glyphPolylines(), and GysinUnderwood.render().

Export

Plotter-safe SVG

The methods were introduced in v1.1.0. Version 1.2.0 adds the ISO page presets used below.

const page = "A3"; const penMap = { frame: 1, type: 2 }; const svg = plot.exportPlotterSVG({ page, penMap }); const json = plot.exportJSON(); const stats = plot.stats({ page, drawSpeed: 20, travelSpeed: 60 }); plot.downloadPlotterSVG("gysin.svg", { page, penMap }); plot.downloadJSON("gysin.json");

exportPlotterSVG() and downloadPlotterSVG() are the safe physical-output route. Built-in pages "A5", "A4", "A3", and "A2" use their exact ISO millimetre dimensions, a 10 mm margin, top-left placement, and a width-fitting scale derived from the canvas. A custom page object with explicit width, height, and mm, cm, or in units remains available for advanced output. The route clips actual path geometry to the page; optimizes travel inside each physical pen group; and writes those groups directly below the SVG root as true Inkscape layers. Each visible layer name starts with its physical number from penMap, for example 1 frame, 2 type; without a map the groups receive a deterministic one-based order. Layers with the same opening number use the same physical pen. In Inkscape, show every layer beginning with 1, install pen 1 and plot; then hide those layers and repeat for 2, 3, and so on. A semantic layer with several stroke colours is split into separate pen groups automatically. The generic exportSVG() and downloadSVG() keep their original screen-oriented defaults and byte-compatible group structure.

page: "A4" is an export option, not a separate A4() drawing function. It leaves the canvas composition unchanged and maps its traces onto the selected physical sheet. Only Plotter Calibration keeps a custom page object because its canvas is a millimetre measuring instrument.

Plotter SVG route optimization is on by default. Pass optimize: false only when physical drawing order matters more than pen-up travel, such as a deliberate wet-ink sequence. Clipping stays mandatory in this route. One pen group may contain at most 2,000 traces during optimization; independent groups no longer share that limit.

Plotter SVG is a centre-line file. It omits per-path opacity and varying stroke widths, and uses one uniform 0.1mm SVG hairline only to keep paths visible. Physical coverage and width come from the installed pen. Plotter metadata always lists alpha, strokeWeight, and pressure-derived screen styling as ignored; they are not plotter settings. The route warns only about actionable physical mapping, such as splitting one semantic layer into several colour groups.

The route never deduplicates geometry: tool: "pen" preserves bleed and explicit repeats, while tool: "blade" removes second and later passages. Repetition is physical geometry, but its ink-darkening effect depends on the actual pen, paper, drying time, and machine.

The Plotter Export poster separates four concerns: canvas colours, the artwork, semantic SVG layers, and physical export. Its black and red layers map to pen 1 and pen 2. Choosing A5, A4, A3, or A2 applies the built-in 10 mm margin, centre-line model, geometric clipping, optimized travel, and width-fitting scale; the 720 × 900 canvas composition does not move.

The Plotter Calibration sheet likewise contains only physical tests: millimetre scale, hatch spacing, geometric disturbance, and one through six real pen passages. It no longer presents alpha, pressure, or SVG stroke width as machine controls.

HPGL

plot.exportHPGL(options) returns the current traces as an HPGL string. plot.downloadHPGL(filename, options) passes the same options to that exporter and downloads the result as a .hpgl file. Both methods are part of the core library; no text or typewriter addon is required.

const hpglOptions = { page: "A4", // physical page + 10 mm margin optimize: true, // reduce pen-up travel per semantic layer penMap: { black: 1, red: 2 }, speed: 20 }; const hpgl = plot.exportHPGL(hpglOptions); // HPGL text plot.downloadHPGL("drawing.hpgl", hpglOptions); // browser download

For physical output, pass both an explicit physical page and optimize: true. HPGL uses the generic export route: without page it falls back to the plot or canvas dimensions, and without optimize: true it preserves drawing order. A preset such as page: "A4" supplies exact millimetres, a 10 mm margin, width-fitting scale, top-left origin, and geometric clipping. Unlike exportPlotterSVG(), HPGL does not require those safeguards or switch optimization on automatically.

penMap maps a semantic layer name or an exact stroke colour to a positive physical pen number. A layer match wins over a colour match; an unmapped trace uses pen 1. The exporter writes SP commands when that number changes. It does not preserve SVG layer names.

HPGL stores coordinates, pen selections, and optional speed—not SVG colours, opacity, varying stroke width, layer names, or page metadata. Physical line width and density therefore come from the installed pen and from repeated geometry. The file starts with initialization and pen selection, draws with PU/PD, and ends by deselecting the pen. It creates a file only: sending that file to a specific machine remains the responsibility of the plotter software or controller.

Shared page model

The page model applies units, margins, origin, rotation, scale, and optional clipping to generic SVG, HPGL, and statistics. Generic route optimization remains opt-in so existing drawing order stays unchanged.

Supported units are px, mm, cm, and in. Origins are top-left and bottom-left. SVG precision accepts decimals from 0 through 12.

Compatibility

Version 1.2.0 targets p5.js 2.x in global and instance mode. The vector and export core is framework-independent, but p5.js 1.x is not part of the tested support contract.

Input safety

Geometry and sampling values must be finite. Invalid dimensions, duplicate shape ids, generated output above the point budget, and paths that would produce more than 100,000 sampled points raise a clear error; probability values are clamped to the 0–1 range. Active bleed rejects a spread below 0.1 to prevent exact duplicate fragments.