Warning
This repository is a work in progress and not yet ready for production use. The API and features may change significantly before the first stable release. Use at your own risk.
p5.millefeuille is a lightweight library that brings Photoshop-style layers to p5.js WebGL sketches. Built on top of p5.Framebuffer, it provides an intuitive API for creating, compositing, and manipulating multiple rendering layers with blend modes, opacity, and masking support.
In your p5.js project, add p5.millefeuille to your index.html using jsdelivr CDN:
<script src="https://cdn.jsdelivr.net/npm/p5.millefeuille@latest/dist/p5.millefeuille.min.js"></script>- p5.js 2.1.1 or higher (for stable
createFramebufferAPI) - WebGL mode (
createCanvas(w, h, WEBGL))
I wanted a layer system that felt familiar to anyone who's used image editing software like Photoshop, GIMP, Procreate, etc, but tailored for creative coding in p5.js.
Millefeuille is not an image editing library. It does not provide tools for pixel-level manipulation, filters, or effects like Photoshop, GIMP, Krita, or other dedicated image editors. Instead, it focuses on managing multiple render targets (layers) and compositing them efficiently using WebGL shaders.
- Layer UI: Optional interactive panel for controlling layers at runtime
- Layer Management: Create, remove, and reorder layers with ease
- 14 Blend Modes: Normal, Multiply, Screen, Add, Subtract, Overlay, Soft Light, Hard Light, Color Dodge, Color Burn, Darken, Lighten, Difference, Exclusion
- Opacity and Visibility: Set per-layer opacity for transparency effects and toggle visibility
- Masking: Apply grayscale masks to layers for selective revealing
let layers;
function setup() {
createCanvas(800, 600, WEBGL);
// Create the layer system using the addon API
layers = createLayerSystem();
// Create layers with method chaining
layers.createLayer('background');
layers.createLayer('effects')
.setOpacity(0.7);
}
function draw() {
// Draw to background layer
layers.begin('background');
clear();
background(30, 30, 60);
layers.end();
// Draw to effects layer
layers.begin('effects');
clear();
// ... draw your effects
layers.end();
// Composite all layers to main canvas
layers.render();
}new p5(sketch => {
let layers;
sketch.setup = function() {
sketch.createCanvas(800, 600, sketch.WEBGL);
// Create layer system using instance method
layers = sketch.createLayerSystem();
layers.createLayer('background');
layers.createLayer('effects').setOpacity(0.7);
};
sketch.draw = function() {
layers.begin('background');
sketch.clear();
sketch.background(30, 30, 60);
layers.end();
layers.begin('effects');
sketch.clear();
// ... draw effects
layers.end();
layers.render();
};
});Creates a new LayerSystem instance (available in global mode or as sketch.createLayerSystem() in instance mode).
Parameters:
options(object, optional) - Layer system configuration
Returns: LayerSystem instance
Example:
// Global mode
const layers = createLayerSystem();
// Instance mode
const layers = sketch.createLayerSystem();Creates a new layer and returns the Layer instance.
Parameters:
name(string, optional) - Human-readable name for the layeroptions(object, optional) - Layer configurationvisible(boolean) - Initial visibility (default: true)opacity(number) - Initial opacity 0-1 (default: 1.0)blendMode(string) - Blend mode from BlendModes (default: NORMAL)width(number) - Custom width (default: canvas width)height(number) - Custom height (default: canvas height)density(number) - Pixel density (default: canvas density)depth(boolean) - Enable depth buffer (default: false)antialias(boolean) - Enable antialiasing (default: false)
Returns: Layer - The created layer instance
Example:
// Create and configure with chaining
layers.createLayer('Effects')
.setBlendMode(BlendModes.ADD)
.setOpacity(0.8);
// Or store the reference
const bg = layers.createLayer('Background');Begin and end drawing to a specific layer.
Parameters:
layerIdOrName(number|string) - The layer ID or name
Example:
// Using layer name (recommended)
layers.begin('Background');
clear();
background(255);
circle(0, 0, 100);
layers.end();
// Using layer reference
const bg = layers.createLayer('Background');
layers.begin(bg.id);
// ... draw
layers.end();Composites all visible layers to the main canvas.
Parameters:
clearCallback(function, optional) - Custom clear function called before compositing
Example:
// Default clear
layers.render();
// Custom clear
layers.render(() => {
background(0, 0, 0, 0); // Transparent background
});Shows or hides a layer.
Parameters:
layerIdOrName(number|string) - The layer ID or name
Returns: Layer|null - The layer for chaining, or null if not found
Example:
layers.hide('Effects'); // Hide layer
layers.show('Effects'); // Show layer
// Chaining
layers.show('Effects').setOpacity(0.5);Sets layer opacity.
Parameters:
layerIdOrName(number|string) - The layer ID or nameopacity(number) - Opacity value 0-1 (will be clamped)
Returns: Layer|null - The layer for chaining, or null if not found
Example:
layers.setOpacity('Effects', 0.5); // 50% opacity
// Chaining
layers.setOpacity('Effects', 0.8).setBlendMode(BlendModes.ADD);Sets layer blend mode.
Parameters:
layerIdOrName(number|string) - The layer ID or namemode(string) - One of:BlendModes.NORMAL,BlendModes.MULTIPLY,BlendModes.SCREEN,BlendModes.ADD,BlendModes.SUBTRACT,BlendModes.OVERLAY,BlendModes.SOFT_LIGHT,BlendModes.HARD_LIGHT,BlendModes.COLOR_DODGE,BlendModes.COLOR_BURN,BlendModes.DARKEN,BlendModes.LIGHTEN,BlendModes.DIFFERENCE,BlendModes.EXCLUSION
Returns: Layer|null - The layer for chaining, or null if not found
Example:
layers.setBlendMode('Effects', BlendModes.ADD);
// Chaining
layers.setBlendMode('Effects', BlendModes.MULTIPLY).setOpacity(0.7);Sets the z-index (draw order) of a layer.
Parameters:
layerIdOrName(number|string) - The layer ID or namezIndex(number) - Z-index value (higher = drawn on top)
Returns: Layer|null - The layer for chaining, or null if not found
Example:
layers.setLayerIndex('Background', 0);
layers.setLayerIndex('Effects', 10);Moves a layer by a relative amount in the stack.
Parameters:
layerIdOrName(number|string) - The layer ID or namedelta(number) - Amount to move (positive = forward, negative = backward)
Returns: Layer|null - The layer for chaining, or null if not found
Example:
layers.moveLayer('Effects', 1); // Move forward by 1Attach or remove a mask from a layer.
Parameters:
layerIdOrName(number|string) - The layer ID or namemaskSource(p5.Framebuffer | p5.Image) - The mask (grayscale, white=opaque, black=transparent)
Returns: Layer|null - The layer for chaining, or null if not found
Example:
const maskBuffer = createFramebuffer({ width, height });
// ... draw to maskBuffer
layers.setMask('Content', maskBuffer);
// Remove mask
layers.clearMask('Content');
// Chaining
layers.setMask('Content', maskBuffer).setOpacity(0.8);Removes a layer and disposes of its resources.
Parameters:
layerIdOrName(number|string) - The layer ID or name
Example:
layers.removeLayer('Effects');Gets a Layer instance by ID or name.
Parameters:
layerIdOrName(number|string) - The layer ID or name
Returns: Layer object or null
Example:
const layer = layers.getLayer('Effects');
console.log(layer.opacity);Gets all layers as an array, sorted by z-index.
Returns: Layer[]
Gets layer information as plain objects.
Returns: Object[] - Array of layer info objects
Example:
const info = layers.getLayerInfo();
console.log(info);
// [{ id: 0, name: 'Background', visible: true, opacity: 1, ... }]Enable or disable automatic layer resizing when canvas size changes.
Parameters:
enabled(boolean) - Auto-resize state
Canvas-synced layers now track both canvas dimensions and pixelDensity(). Layers that were created or resized with custom width, height, or density stay untouched so you can maintain bespoke render targets.
Disposes of all layers and resources. Call when done with the layer system.
Creates an interactive UI panel for controlling layers.
Parameters:
options(object, optional) - UI configurationposition(string) - Panel position: 'top-right' (default), 'top-left', 'bottom-right', 'bottom-left'width(number) - Panel width in pixels (default: 280)collapsible(boolean) - Allow collapsing the panel (default: true)draggable(boolean) - Allow dragging the panel (default: true)
Returns: LayerUI instance
Example:
// Basic UI
layers.createUI();
// Custom UI
layers.createUI({
position: 'top-left',
width: 320
});Helpful options:
thumbnailAutoUpdate(boolean, defaultfalse) — Whentrue, thumbnails update every frame. Whenfalse(default), thumbnails only refresh when clicked.thumbnailUpdateEvery(number, default0) — Update thumbnails every N frames. Set to30for updates twice per second at 60fps. When0, updates only occur on click (unlessthumbnailAutoUpdateistrue).
UI Features:
- Layer thumbnails: Visual previews of each layer's content
- Click to update: Click any layer to refresh its thumbnail (thumbnails only update on click to maintain performance)
- Visibility toggle: Show/hide layers with checkbox
- Opacity control: Adjust layer opacity with slider
- Blend mode selector: Change layer blend modes
- Collapsible: Minimize the panel when not in use
- Draggable: Reposition the panel anywhere on screen
Available blend modes in BlendModes:
- NORMAL - Standard alpha blending
- MULTIPLY - Darkens colors
- SCREEN - Lightens colors (approximated in WebGL)
- ADD - Additive blending (great for glows)
- SUBTRACT - Subtractive blending
- OVERLAY - Combines Multiply and Screen
- SOFT_LIGHT - Soft light effect
- HARD_LIGHT - Hard light effect
- COLOR_DODGE - Brightens colors
- COLOR_BURN - Darkens colors
- DARKEN - Keeps darker colors
- LIGHTEN - Keeps lighter colors
- DIFFERENCE - Inverts colors based on difference
- EXCLUSION - Similar to Difference but lower contrast
See examples/01-basic.html for a complete example with multiple layers.
See examples/02-blend-modes.html for interactive blend mode demonstration.
See examples/03-thumbnail-cropping/index.html to watch the Layer UI crop thumbnails by finding the smallest box of non-transparent pixels, adding gentle padding, and smoothing the result over a few frames.
See examples/04-full-window/index.html for a responsive sketch that drives resizeCanvas() from the built-in windowResized() callback so every framebuffer and the compositor stay razor sharp across window and pixel-density changes.
- Minimize layer count: More layers = more compositing passes
- Match layer size to needs: Smaller layers for UI elements
- Reuse layers: Clear and redraw instead of creating new layers each frame
- Disable unused layers: Set
visible: falseinstead of removing - Use appropriate blend modes: NORMAL is fastest
- LayerSystem: Manages the layer stack and coordinates rendering
- Layer: Wraps a
p5.Framebufferwith metadata (opacity, blend mode, etc.) - Compositor: Handles the rendering pipeline using custom shaders for all layer compositing
- WebGL mode only (no 2D renderer support)
- Limited to WebGL-supported blend modes
- Masks must be same size as layer (or will be scaled)
- No adjustment layers or smart objects (not in MVP scope)
Contributions welcome! Please open an issue on GitHub.
LGPL-2.1 License - see LICENSE file for details
- p5.layers by Oliver Steele (2D renderer)
- p5.js Layered Rendering tutorial
"Millefeuille" is French for "a thousand layers," referring to a classic French pastry made of many thin layers of puff pastry and cream.
p5.millefeuille - Because every great sketch is built in layers.