p5.brush.js
p5.brush.js adds natural drawing tools to p5.js — pencils, charcoal, markers, watercolor fills, hatch patterns, and vector fields that bend strokes organically. It's built for generative art and high-resolution printing.
Visit the library website here! (more examples soon)
🖌️ Try the interactive Brush Maker →
Design custom brushes with live preview and generate ready-to-paste brush.add() code.
🌊 Try the interactive Flow Field Generator →
Design custom vector fields with live preview and generate ready-to-paste brush.addField() code.
▶️ Check the teaser live →
Live on the p5.js Web Editor, where you can read and edit code.
Two builds
p5.brush ships in two flavors:
| | p5 build | Standalone build |
|---|---|---|
| File | dist/p5.brush.js | dist/brush.js |
| Requires | p5.js 2.x + WEBGL canvas | Nothing — WebGL2 browser only |
| Canvas setup | createCanvas(w, h, WEBGL) | brush.createCanvas(w, h) |
| Transforms | p5's push/pop, translate, rotate, scale | brush.push/pop, brush.translate, etc. |
| Angle mode | Follows p5's angleMode() | brush.angleMode(brush.DEGREES \| brush.RADIANS) |
| Seeding | randomSeed() / noiseSeed() | brush.seed() / brush.noiseSeed() |
| Frame flush | Automatic | brush.render() at end of each frame |
| Clear | p5's background() | brush.clear(color?) |
This README covers the p5 build. For the standalone build see docs/standalone.md.
Table of Contents
- Installation
- Quick Start
- Core Concepts in 1 Minute
- Features
- Reference
- Examples
- Contributing
- License
- Acknowledgements
Installation
Important note: p5.brush requires p5.js 2.x or higher.
Local Installation
To set up your project, add p5.min.js and p5.brush.js to your HTML file. You can download the latest version of the p5.brush.js library in the dist folder.
Place the script tags in the following order:
<script src="path_to/p5.min.js"></script>
<script src="path_to/p5.brush.js"></script>
Replace path_to with the actual path to the script in your project directory or the URL if you are using a CDN.
Use a hosted version of the p5.brush.js library
Alternatively, you can link to a p5.brush.js file hosted online. All versions are stored in a CDN (Content Delivery Network). You can find a history of these versions in the p5.js CDN. In this case you can change the link to:
<!-- Online version of p5.brush -->
<script src="https://cdn.jsdelivr.net/npm/p5.brush@latest"></script>
Install with NPM and other modular-based apps
Install the npm package. p5.brush requires p5.js as a peer dependency for the p5 build.
npm install p5.brush --save
The package exposes two subpaths — pick the one that matches your build:
// p5 build (default) — requires p5.js 2.x
import * as brush from 'p5.brush'
// Standalone build — no p5.js required
import * as brush from 'p5.brush/standalone'
If you are using p5 and p5.brush as modules, you will need to use instance mode. Read below.
Note for p5 instance mode
In instance mode, p5 functions are scoped to a variable instead of the global namespace — useful when mixing with other libraries or using p5 as an ES module.
Call brush.instance(p) inside your sketch function before setup and draw. After that, all brush.* calls work normally without any p. prefix.
const sketch = (p) => {
// Tell p5.brush which p5 instance to use
brush.instance(p);
p.setup = () => {
// Canvas must be in WEBGL mode — brush initializes automatically
p.createCanvas(700, 410, p.WEBGL);
};
p.draw = () => {
p.background(240);
brush.set("HB", "#333", 1);
brush.line(100, 100, 400, 300);
};
};
new p5(sketch);
Quick Start
If you already know the basics of p5, this is the shortest path to drawing with p5.brush:
- Create a
WEBGLcanvas. - Pick a brush with
brush.set(name, color, weight). - Draw with a primitive like
brush.line(),brush.rect(), orbrush.circle().
function setup() {
createCanvas(700, 410, WEBGL);
background("#f6f1e8");
brush.scaleBrushes(3);
brush.set("HB", "#2f2a26", 1.4);
brush.line(-220, -80, 180, 40);
brush.fill("#d7c3a3", 120);
brush.noStroke();
brush.circle(120, 20, 70);
brush.set("rotring", "#1f4b99", 0.8);
brush.noFill();
brush.hatch(7, 35);
brush.rect(-140, 40, 120, 90, "center");
}
If you are new to the library, a good first sequence is:
brush.scaleBrushes(...)if you are using built-in brushesbrush.set(...)to choose the stroke brushbrush.fill(...)orbrush.hatch(...)if you want interior texturebrush.line(),brush.rect(),brush.circle(), orbrush.polygon()to draw
Core Concepts in 1 Minute
- p5.brush follows p5's drawing model: first you set drawing state, then you draw shapes.
brush.set(),brush.stroke(),brush.strokeWeight(),brush.fill(),brush.hatch(), and related functions configure how upcoming shapes should look.brush.line(),brush.rect(),brush.circle(),brush.arc(),brush.beginShape(), andbrush.polygon()actually draw geometry.brush.noStroke(),brush.noFill(),brush.noHatch(), andbrush.noWash()turn parts of that state back off.- Vector fields are optional. If you never call
brush.field(...), your shapes still draw normally. - Most sketches only need a small subset of the API: choose a brush, maybe add fill or hatch, then draw primitives.
- Style: choose how marks look with
brush.set(),brush.fill(),brush.hatch(), and related state functions - Geometry: draw lines and shapes with
brush.line(),brush.rect(),brush.circle(),brush.beginShape(), and more - Advanced control: add vector fields, custom brushes, clipping, buffers, and classes when you need them
Features
p5.brush.js enhances the p5.js framework with a set of tools that allow for sophisticated drawing and rendering techniques.
- Custom Configuration: Customize your drawing strokes with the ability to select different buffers and leverage a custom random number generator to introduce variability in procedural designs.
- Vector Field Integration: Direct the motion of your brush strokes with vector fields, crafting fluid, dynamic visuals within your sketches.
- Dynamic Brush System: Select from an array of brushes, each offering distinct characteristics, to create a variety of effects and textures.
- Brush and Field Management: Manage and switch between brushes and vector fields with ease, adapting to the needs of your project seamlessly.
- Extensibility: Expand the library's capabilities by integrating your own custom brushes and vector fields, tailoring the toolset to your artistic vision.
- Custom Brush Tips: Load and use your own custom brush tip assets.
- Interactive Brush Tips: Utilize pressure-sensitive brush tips for interactive drawing, adding a level of responsiveness to your canvas work.
- Hatch Patterns: Implement hatching techniques with precision control over the patterns' density and orientation, enhancing your artwork with automated detailing.
- Intuitive Spline and Curve Generation: Generate smooth and accurate curves and splines effortlessly, simplifying the process of intricate path creation.
- Watercolor Fill System: Achieve the subtle nuances of watercolor with a digital fill system designed to blend and diffuse colors in a naturally fluid way.
Reference
p5.brush.js provides a comprehensive API for creating complex drawings and effects. Below are the categorized functions and classes available in the library.
Table of Functions
| Section | Functions | Section | Functions |
|---|---|---|---|
| Configuration | brush.load(), brush.scaleBrushes(), brush.instance() | Fill Operations | brush.fill(), brush.noFill(), brush.wash(), brush.noWash(), brush.fillBleed(), brush.fillTexture() |
| Vector Fields | brush.field(), brush.noField(), brush.refreshField(), brush.listFields(), brush.addField(), brush.wiggle() | Hatch Operations | brush.hatch(), brush.noHatch(), brush.hatchStyle(), brush.mass(), brush.noMass() |
| Brush Management | brush.box(), brush.add(), brush.clip(), brush.noClip() | Primitives | brush.line(), brush.flowLine(), brush.beginStroke(), brush.move(), brush.endStroke(), brush.spline(), brush.rect(), brush.circle(), brush.arc(), brush.beginShape(), brush.vertex(), brush.endShape(), brush.polygon() |
| Stroke Operations | brush.set(), brush.pick(), brush.stroke(), brush.noStroke(), brush.strokeWeight() | Exposed Classes | brush.Polygon(), brush.Plot(), brush.Position() |
| | | Utility Functions | upgrade notes, angle behavior |
Configuration
This section covers functions for initializing the drawing system and configuring how the library behaves in your sketch. In particular, brush.scaleBrushes() is usually important when you use the built-in brushes.
Seeding: p5.brush automatically hooks into p5'srandomSeed()andnoiseSeed(). Calling either of those functions seeds both p5 and the library simultaneously — no separatebrush.seed()call is needed.
brush.load(buffer)
createCanvas(..., WEBGL). Pass a p5.Graphics buffer or an active p5.Framebuffer to draw into that target instead; call brush.load() with no argument to switch back to the main canvas. Framebuffers created from p5.Graphics are not supported.
- Parameters:
- buffer (p5.Graphics | p5.Framebuffer): Optional. An offscreen target to draw into.
- How to use it:
- p5.Graphics: create it with WEBGL, call brush.load(pg), draw with brush functions, then call brush.load() to restore the main canvas before presenting it with image(pg, ...).
- p5.Framebuffer: create it from the main sketch with createFramebuffer(...), enter its draw() or begin() / end() scope, call brush.load(fb) while it is active, draw with brush functions, then call brush.load() again after leaving the framebuffer scope.
- brush.load(...) only changes the target used by p5.brush. If you also use native p5 drawing calls, call them on the same target yourself, for example pg.background(...) for p5.Graphics.
- pg.createFramebuffer() is not supported.
- Example (draw into a buffer):
function setup() {
createCanvas(400, 400, WEBGL);
brush.load();
brush.set("HB", "black", 1);
brush.rect(40, 40, 150, 100);
// Switch to a buffer
let buffer = createGraphics(200, 300, WEBGL);
brush.load(buffer);
brush.set("HB", "black", 1);
brush.rect(40, 40, 150, 100);
image(buffer, 20, 40);
// Switch back to main canvas
brush.load();
}
- Example (draw into a framebuffer):
function setup() {
createCanvas(400, 400, WEBGL);
brush.load();
const layer = createFramebuffer({ width: 200, height: 200 });
layer.draw(() => {
brush.load(layer);
brush.set("HB", "black", 1);
brush.circle(100, 100, 80);
});
brush.load();
image(layer, 20, 20);
}
- Typical workflows:
// p5.Graphics
const pg = createGraphics(300, 200, WEBGL);
pg.background(250);
brush.load(pg);
brush.set("HB", "black", 1);
brush.circle(150, 100, 70);
brush.load();
image(pg, 20, 20);
// p5.Framebuffer
const fb = createFramebuffer({ width: 300, height: 200 });
fb.draw(() => {
background(250);
brush.load(fb);
brush.set("HB", "black", 1);
brush.circle(150, 100, 70);
});
brush.load();
image(fb, 20, 240);
brush.scaleBrushes(scale)
scale (Number): The scaling factor to be applied to the brush parameters.
- Important: If you are using the built-in brushes, brush.scaleBrushes() is usually not optional in practice. Without it, the built-in brushes will often look much too small or otherwise wrong for your canvas size. For a 600x600 canvas, brush.scaleBrushes(3) is a good starting point, but the final value should still be confirmed visually.
- Note: This affects the brushes that already exist when you call it. If you only want the built-in brushes scaled, call it before adding custom brushes. If you add custom brushes later, call brush.scaleBrushes() again to scale them too.
- Usage:
// Usually needed when working with built-in brushes
brush.scaleBrushes(3);
Use brush.scaleBrushes() early in setup() so the built-in brushes match your canvas scale before you start drawing.
brush.instance(p)
setup and draw when using p5 in instance mode. Tells p5.brush which p5 instance to render into. After calling this, all brush.* functions work normally — no need to prefix them with p..
- Parameters:
- p (p5): The p5 instance passed as the argument to your sketch function.
- Example:
const sketch = (p) => {
// Must be called before setup/draw
brush.instance(p);
p.setup = () => {
// Canvas must be WEBGL
p.createCanvas(700, 410, p.WEBGL);
brush.load();
};
p.draw = () => {
p.background(240);
brush.set("HB", "#333", 1);
brush.line(100, 100, 400, 300);
};
};
new p5(sketch);
Vector Fields
Vector fields let you bend and redirect strokes so they follow a flow across the canvas. If you do not use them, p5.brush still works like a normal drawing library.
#### Basic vector-field functions
brush.field(name)
name (String): The identifier for the vector field to be activated. This can be a name of one of the predefined fields or a custom field created with brush.addField().
- Default Fields: The library comes with a set of built-in vector fields: hand, curved, zigzag, waves, seabed, spiral, and columns. These, as well as any custom fields added, can be activated using this function.
- Usage:
// To activate the "waves" vector field
brush.field("waves");
// To activate a custom vector field named "myCustomField"
brush.field("myCustomField");
Once a vector field is activated, it affects how the subsequent shapes are drawn, aligning them with its directional flow, unless stated otherwise in the documentation
brush.noField()
brush.noField();
brush.wiggle(wiggle)
"hand" vector field with a given wiggle intensity. A shorthand for adding a subtle hand-drawn wobble to strokes without calling brush.field("hand") directly.
- Parameters:
- wiggle (Number): Intensity of the wobble, e.g. 1–10.
- Usage:
brush.wiggle(3);
brush.refreshField(time)
time (Number): The time input for the vector field generator function, typically related to the frame count.
- Usage:
// In the draw loop, refresh the vector field based on the frame count
function draw() {
brush.refreshField(frameCount / 10);
// Additional drawing code
}
This function will invoke the generator function of the active vector field with the provided time argument, allowing the field to evolve and create fluid, dynamic animations in the rendering.
brush.listFields()
brush.addField().
- Returns: Array - An array of vector field names.
- Usage:
let fieldNames = brush.listFields();
for (let name of fieldNames) {
console.log(name);
}
#### Advanced vector-field functions
brush.addField(name, generatorFunction, options)
brush.field(name) just like any built-in field.
- Parameters:
- name (String): Any name you want, e.g. "myField".
- generatorFunction (Function): A function (t, field) => field that fills every cell of the grid with an angle and returns it. t is a time value you can use for animation (pass it via brush.refreshField(t)).
- options (Object): Optional configuration for custom field angles.
- angleMode: "degrees" (default) or "radians". This controls how the values written into field[column][row] are interpreted.
- How the grid works: field is a 2D array — field[column][row]. You loop through every column and row and set each cell to an angle. By default those angles are interpreted as degrees. Small angles (±10°) make subtle curves; large ones (±90° or more) make dramatic bends.
- Simplest example — a diagonal flow that slowly rotates over time:
brush.addField("diagonal", function(t, field) {
for (let col = 0; col < field.length; col++) {
for (let row = 0; row < field[0].length; row++) {
field[col][row] = 45 + t * 10; // 45° angle, changes with time
}
}
return field; // always return field at the end!
});
brush.field("diagonal"); // activate it
- Tip: You don't have to use t at all if you don't need animation. Just fill the grid and return it.
Brush Management
This section covers choosing brushes, adding your own brushes, and clipping where strokes are allowed to appear.
brush.box()
Array - An array listing the names of all brushes.
- Default Brushes: The library includes a default set of 11 brushes: 2B, HB, 2H, cpencil, pen, rotring, spray, marker, marker2, charcoal, and hatch_brush (for clean hatching).
- Usage:
// Retrieve a list of all available brush names
let brushes = brush.box();
console.log(brushes); // Logs the array of brush names
brush.box() allows you to explore and select from the various brushes, facilitating the choice of the appropriate brush for different artistic needs.
brush.add(name, params)— Design your brush visually with the Brush Maker ↗
brush.set("myBrush", color, size).
- Parameters:
- name (String): A name for your brush — pick anything you like.
- params (Object): An object with the brush settings:
- type: The kind of brush tip. Choose one: "default" (pencil-like), "spray" (scattered dots), "marker" (flat solid), "custom" (you draw the tip shape), "image" (use a photo as the tip).
- weight: How thick the brush is, in canvas units.
- scatter: How much the stroke wobbles sideways. Higher = more spread. (in canvas units)
- sharpness: A number from 0 to 1. Lower = softer/fuzzier edge. Only matters for "default" type.
- grain: Controls how dense the texture is. Higher = smoother, more continuous line. Only matters for "default" and "spray" types.
- opacity: How opaque each mark is, from 0 to 255. Pressure also affects this.
- spacing: Gap between brush tip stamps along the stroke. 1 means no overlap, lower values create denser strokes.
- pressure: Controls how the brush size changes from start to end of a stroke. Use a simple array — the easiest way:
- [start, end] — e.g. [2, 0.5] starts thick and gets thin.
- [start, middle, end] — e.g. [0.5, 2, 0.5] starts thin, swells in the middle, ends thin.
- Or a function (t) => value where t goes from 0 (start) to 1 (end), for full control.
- These simple/custom pressure modes include a subtle amount of per-stroke variation automatically, so repeated strokes feel less mechanical.
- Advanced option for power users: use the library's built-in Gaussian pressure profile explicitly with an object like { mode: "gaussian", curve: [0.15, 0.2], min_max: [1.1, 0.9] }.
curve adjusts the wobble and asymmetry of the pressure envelope, while min_max sets the mapped pressure range. This is the same family of pressure logic used by the built-in brushes.
- tip: Only for "custom" type. A function that draws the tip shape — see Custom tip brushes note below.
- image: Only for "image" type. An object with a src property pointing to your image file: { src: "./tip.jpg" }.
- rotate: How the tip rotates as it moves. "none" keeps it fixed, "natural" follows the stroke direction, "random" spins randomly.
- markerTip: Only for "marker", "custom", and "image" types. Set to false to disable the extra soft tip buildup those brushes add at the start and end of each stroke. Defaults to true.
- noise: Controls per-stroke opacity variation. Each time a stroke is drawn, the brush samples a Gaussian to randomly shift the whole stroke slightly lighter or darker than its base alpha — giving repeated strokes an organic, non-mechanical feel. 0 disables the variation (every stroke is identical in opacity). 1 is the maximum variation. Defaults to 0.3.
- Usage:
// Image brush — use a photo as the brush tip.
// IMPORTANT: use "await" so the image loads before drawing,
// and make sure your setup() function is declared "async"!
async function setup() {
createCanvas(800, 800, WEBGL);
brush.load();
await brush.add("watercolor", {
type: "image",
weight: 10,
scatter: 2,
opacity: 30,
spacing: 1.5,
pressure: [1, 0.5], // starts thick, ends thin
image: { src: "./brush_tips/brush.jpg" },
rotate: "random",
markerTip: false,
});
// Now you can draw with it!
brush.set("watercolor", "blue", 1);
brush.line(100, 100, 400, 100);
}
// Advanced: explicitly use the built-in Gaussian pressure mode
brush.add("soft-pencil", {
type: "default",
weight: 0.3,
scatter: 0.6,
sharpness: 0.3,
grain: 10,
opacity: 170,
spacing: 0.1,
pressure: {
mode: "gaussian",
curve: [0.15, 0.2],
min_max: [1.1, 0.9],
},
rotate: "none",
});
Image brushes only: brush.add() returns a Promise when type is "image" — you must await it so the image finishes loading before you start drawing. For this to work, your setup() function must be declared async. For all other brush types, no await is needed.
Custom tip brushes: The tip function receives _m, a p5.Graphics buffer. You draw inside it using normal p5 commands. The coordinate space is 100×100 units with the origin at the centre, so shapes are drawn around (0, 0) and edges fall around ±50 units. The library converts the result to a mask: dark fills become opaque, light or white becomes transparent — so you define the shape in dark tones, and the color from brush.set() or brush.stroke() is applied at render time. Any p5 drawing command works: rect, circle, ellipse, line, triangle, beginShape/vertex/endShape, push/pop, translate, rotate, scale, and so on.
brush.add("diamond", {
type: "custom",
weight: 5,
scatter: 0.08,
opacity: 23,
spacing: 0.6,
pressure: [0.5, 1.5, 0.5], // thin → thick → thin
tip: (_m) => {
_m.rotate(45); // degrees, follows p5 angleMode
_m.rect(-1.5, -1.5, 3, 3);
},
rotate: "natural",
markerTip: false,
});
Using the standalone build? The tip function works differently there — _m is not a p5.Graphics. See docs/standalone.md → Custom tip brushes.
brush.clip(clippingRegion)
brush.clip() is called. The clipping remains in effect for all strokes drawn after the call to brush.clip() until brush.noClip() is used.
- Parameters:
- clippingRegion (Array): An array defining the clipping region as [x1, y1, x2, y2], with (x1, y1) and (x2, y2) being the corners of the clipping rectangle.
- Usage:
// Set a clipping region
brush.clip([10, 10, 250, 200]);
// Draw a line - it will be clipped according to the region
brush.line(100, 90, 300, 40);
// Remove the clipping region
brush.noClip();
// Draw another line - it will not be clipped
brush.line(0, 0, 200, 300);
brush.noClip()
// Disable the clipping region
brush.noClip();
Stroke Operations
These functions control stroke appearance: which brush is active, what color it uses, and how thick it should be.
brush.set(brushName, color, weight)
brushName (String): The name of the brush to be used.
- color (String|p5.Color): The color for the brush, which can be specified as a HEX string or a p5.Color object.
- weight (Number): The weight or size of the brush.
- Note: This function will automatically activate stroke mode for subsequent geometries.
- Usage:
// Set the "HB" brush with a specific blue color and weight factor 1
brush.set("HB", "#002185", 1);
By using brush.set(), you can quickly switch between different brushes, colors, and sizes, allowing for dynamic and varied stroke applications in your artwork.
- brush.pick(brushName)
- Description: Selects the current brush type based on the specified name. This function is used to change the brush type without altering its color or weight.
- Parameters:
- brushName (String): The name of the brush to set as current.
- Usage:
// Set the current brush to "charcoal"
brush.pick("charcoal");
Use brush.pick() to switch between different brush types while maintaining the current color and weight settings.
brush.stroke(r, g, b)orbrush.stroke(color)
r (Number|String|p5.Color): The red component of the color, a CSS color string, or a p5.Color object.
- g (Number): Optional. The green component of the color.
- b (Number): Optional. The blue component of the color.
- Usage:
// Set the brush color using RGB values
brush.stroke(105, 111, 34);
// Or set the brush color using a HEX string
brush.stroke("#002185");
Use brush.stroke() to define the color of your brush strokes, enabling a diverse palette for your artwork.
brush.noStroke()
// Disable stroke for the upcoming shapes
brush.noStroke();
brush.noStroke() is essential for creating drawings where only fill and no outline is desired.
brush.strokeWeight(weight)
weight (Number): The weight to set for the brush.
- Returns: None.
- Usage:
// Set the brush stroke weight to 2.3 times the base size
brush.strokeWeight(2.3);
brush.strokeWeight() provides the flexibility to easily adjust the thickness of your brush strokes, enhancing the expressiveness of your drawing tools.
Fill Operations
These functions control how closed shapes are filled. You can use watercolor-style fills, flat wash fills, and adjust bleed or texture.
brush.fill(a, b, c, d)orbrush.fill(color, opacity)
a (Number|String|p5.Color): The red component of the color or grayscale value, a CSS color string, or a p5.Color object.
- b (Number): Optional. The green component of the color or grayscale opacity if two arguments are used.
- c (Number): Optional. The blue component of the color.
- d (Number): Optional. The opacity of the color, 0 - 255.
- Usage:
// Set the fill color using RGB values and opacity
brush.fill(244, 15, 24, 75);
// Or set the fill color using a HEX string and opacity
brush.fill("#002185", 110);
brush.fill() allows for detailed control over the color and transparency of the fill.
brush.noFill()
// Disable fill for the upcoming shapes
brush.noFill();
brush.wash(color, opacity)
fill(), wash() does not simulate watercolor bleed or texture; it draws a direct flat color with the given opacity.
- Parameters:
- color (String|p5.Color): The wash color.
- opacity (Number): Opacity from 0 to 255.
- Usage:
brush.wash("#f7e4a0", 255);
brush.circle(width / 2, height / 2, 180);
brush.noWash();
brush.noWash()
brush.noWash();
brush.fillBleed(strength, direction, angle)
strength (Number): The intensity of the bleed effect, ranging from 0 to 1.
- direction (String): Optional. "out" or "in". Defines the direction of the bleed effect.
- angle (Number): Optional. Sets the watercolor wash direction in the current angle mode; omit for random direction.
- Usage:
brush.fillBleed(0.3, "out", 0); // left to right
brush.fillTexture(textureStrength, borderIntensity, scatter?)
textureStrength (Number): The texture of the fill effect, ranging from 0 to 1.
- borderIntensity (Number): The intensity of the border watercolor effect, ranging from 0 to 1.
- scatter (Boolean, optional, default true): Whether to draw the sparse scattered polygon layers that add edge texture noise. Set to false for a cleaner gradient trim effect.
- Usage:
// Set the fill texture and border intensity
brush.fillTexture(0.6, 0.4);
// Disable scatter layers for a cleaner gradient
brush.fillTexture(0.6, 0.4, false);
Hatch Operations
These functions control hatching and massing inside shapes, using repeated lines or layered marks to build tone and texture.
brush.hatch(dist, angle, options)
dist (Number): The distance between hatching lines, in canvas units.
- angle (Number): The angle at which hatching lines are drawn. It is interpreted using p5's current angleMode() when brush.hatch() is called.
- options (Object): Optional settings to affect the hatching style, including:
- rand: Randomness in line placement (0 to 1 or false).
- continuous: Whether to connect the end of a line with the start of the next.
- gradient: Modifies the distance between lines to create a gradient effect (0 to 1 or false).
- Defaults to {rand: false, continuous: false, gradient: false}.
- Usage:
// Set hatching with specific distance, angle, and options
brush.hatch(5, 30, {rand: 0.1, continuous: true, gradient: 0.3});
brush.noHatch()
// Disable hatching for upcoming shapes
brush.noHatch();
brush.hatchStyle(brushName, color, weight)
brushName (String): The name of the brush to use for hatching.
- color (String|p5.Color): The color for the brush, either as a CSS string or a p5.Color object.
- weight (Number): The weight or size of the brush for hatching.
- Usage:
brush.hatchStyle("rotring", "green", 1.3);
brush.mass(brushName, color, options)
brushName (String): Brush used for the mass strokes.
- color (String|p5.Color): Color of the mass.
- options (Object): Optional settings:
- precision: 0 to 1. Higher values reduce jitter and randomness.
- strength: 0 to 1. Controls how many of the three internal layers are drawn.
- gradient: 0 to 1. Passed through to hatch generation for spacing variation.
- outline: true or false. If true, the first polygon is also outlined.
- Usage:
brush.mass("pastel", "#4b6cb7", {
precision: 0.55,
strength: 0.9,
gradient: 0.35,
outline: true,
});
brush.circle(width / 2, height / 2, 180);
brush.noMass()
brush.noMass();
In essence, the hatching system activates hatches for subsequent shapes, similarly to stroke and fill operations. However, you can also directly hatch multiple objects at once, including their intersections, as described below.
brush.hatchArray(polygons)
polygons (Array|Object): The polygon(s) to apply the hatching. Can be a single polygon object or an array of polygon objects.
- Note: This is not the main, but an alternative way of applying hatches. Read above.
- Usage:
// Define an array of polygons (reference in the classes section)
let myPolygons = []
for (let i = 0; i < 10; i++) {
// We're creating 10 random triangles here
let p = new brush.Polygon([
[random(width), random(height)],
[random(width), random(height)],
[random(width), random(height)],
])
myPolygons.push(p)
}
// Create hatching across specified polygons
brush.hatchArray(myPolygons);
brush.hatchArray() provides an efficient way to apply complex hatching patterns to a set of defined shapes.
brush.massArray(polygons)
mass() state to a single brush.Polygon or an array of brush.Polygon objects as one combined multi-layer gesture. The array is treated as combined layers: the first polygon defines the outer boundary, additional polygons act as holes or inner layers via even-odd logic.
- Parameters:
- polygons (Array|Object): A single brush.Polygon or an array of brush.Polygon objects to mass.
- Note: Like hatchArray(), this is an alternative to letting shapes pick up mass state automatically — use it when you need to treat multiple polygons as a single gesture.
- Usage:
// Define two nested polygons — outer boundary and inner hole
let outer = new brush.Polygon([[50,50],[350,50],[350,350],[50,350]]);
let inner = new brush.Polygon([[100,100],[300,100],[300,300],[100,300]]);
let myPolygons = [outer, inner];
// Apply mass to both as one combined gesture
brush.mass("HB", "#002185", { strength: 1 });
brush.massArray(myPolygons);
brush.massArray() treats the array as layered geometry, so the inner polygon creates a hole via even-odd logic rather than being massed independently.
Primitives
These are the functions that actually draw lines, paths, and shapes using the current stroke, fill, and hatch settings.
Lines, Strokes, Splines, and Plots
The following functions are only affected by stroke() operations, completely ignoring fill() and hatch().
brush.line(x1, y1, x2, y2)
noStroke() has been called.
- Parameters:
- x1 (Number): The x-coordinate of the start point.
- y1 (Number): The y-coordinate of the start point.
- x2 (Number): The x-coordinate of the end point.
- y2 (Number): The y-coordinate of the end point.
- Usage:
// Set the brush color and draw a line
brush.stroke("red");
brush.line(15, 10, 200, 10);
brush.flowLine(x, y, length, dir)
x (Number): The x-coordinate of the starting point.
- y (Number): The y-coordinate of the starting point.
- length (Number): The length of the line.
- dir (Number): The direction in which to draw the line, measured anticlockwise from the x-axis and interpreted using the current p5 angleMode().
- Usage:
// Set a vector field and draw a flow line
brush.field("seabed");
brush.flowLine(15, 10, 185, 0);
These three functions provide advanced control over the creation of strokes/paths, allowing for custom pressure and direction at different points along the path. Think of them as bodily movements performed with the hands. You can create two types of strokes: "curve" or "segments". For curved strokes, the curvature at any point is interpolated between the nearest control points.
These functions allow for the creation of strokes with varied pressures and directions, mimicking the organic nature of hand-drawn strokes. For an application of these principles, see: Enfantines II
brush.beginStroke(type, x, y)
type (String): The type of the stroke, either "curve" or "segments".
- x (Number): The x-coordinate of the starting point of the stroke.
- y (Number): The y-coordinate of the starting point of the stroke.
- Usage:
// Begin a new curve stroke
brush.beginStroke("curve", 15, 30);
brush.move(angle, length, pressure)
brush.beginStroke() and brush.endStroke() to outline the stroke's trajectory and characteristics.
- Parameters:
- angle (Number): The initial angle of the segment, relative to the canvas, measured anticlockwise from the x-axis.
- length (Number): The length of the segment.
- pressure (Number): The pressure at the start of the segment, influencing properties like width.
- Usage:
brush.move(30, 150, 0.6);
brush.move(75, 40, 1.1);
brush.endStroke(angle, pressure)
angle (Number): The angle of the curve at the end point of the stroke path.
- pressure (Number): The pressure at the end of the stroke.
- Usage:
// Complete the stroke with a specific angle and pressure
brush.endStroke(-45, 0.8);
brush.spline(array_points, curvature)
array_points (Array[x, y] or [x, y, pressure]. The optional pressure value at each point influences brush width along the curve.
- curvature (Number): Optional. The curvature of the spline, ranging from 0 to 1. A value of 0 produces straight segments.
- Returns: brush.Plot — the rendered plot. Store it to apply .hatch(), .mass(), .fill(), or .draw() later at a different position.
- Note: This is a simplified alternative to beginShape() - endShape() operations, useful for certain stroke() applications.
- Usage:
let points = [[30, 70], [85, 20, 1.5], [130, 100], [180, 50]];
brush.spline(points, 0.5);
Shapes and Polygons
The following functions are affected by stroke(), fill() and hatch() operations.
brush.rect(x, y, w, h, mode)
x (Number): The x-coordinate of the rectangle.
- y (Number): The y-coordinate of the rectangle.
- w (Number): The width of the rectangle.
- h (Number): The height of the rectangle.
- mode (String): Optional. "corner" (default) positions (x, y) at the top-left corner. "center" draws the rectangle centered at (x, y).
- Usage:
brush.noStroke();
brush.noHatch();
brush.fill("#002185", 75);
brush.rect(150, 100, 50, 35, "center");
brush.circle(x, y, radius, r)
x (Number): The x-coordinate of the circle's center.
- y (Number): The y-coordinate of the circle's center.
- radius (Number): The radius of the circle.
- r (Number|Boolean): Optional. Hand-drawn irregularity amount. Values around 0 to 1 give subtle variation; true behaves like a strong irregularity.
- Returns: [plot, offsetX, offsetY] — the brush.Plot and the top-left drawing offset. Pass these to plot.hatch(offsetX, offsetY), plot.mass(offsetX, offsetY), etc. to reuse the geometry.
- Usage:
brush.circle(100, 150, 75, true);
brush.arc(x, y, radius, start, end)
fill() is not applied.
- Parameters:
- x (Number): The x-coordinate of the center.
- y (Number): The y-coordinate of the center.
- radius (Number): The radius of the arc.
- start (Number): Start angle interpreted using the current p5 angleMode().
- end (Number): End angle interpreted using the current p5 angleMode().
- Returns: brush.Plot — the rendered plot, or null when start equals end (zero sweep). Store it to redraw or apply effects at a different position.
- Usage:
brush.arc(200, 200, 50, 0, Math.PI);
These three functions perform similarly to the p5.js beginShape(), vertex(), and endShape() functions, although their curvature calculation is very different. They allow you to draw custom shapes with fine control over brush pressure at different points of the perimeter.
brush.beginShape(curvature)
curvature (Number): Optional. A value from 0 to 1 that defines the curvature of the shape's edges.
... (README truncated for length)