Paper.js API Reference
This page documents Paper.js APIs available in PinePaper for building MCP tools and advanced integrations. Paper.js is installed globally, so all classes are available on the paper namespace or directly (e.g., Point, Path, Color).
When to Use Paper.js vs PinePaper API
| Use Case | Recommended API |
|---|---|
| Create standard shapes (text, circle, star, etc.) | app.create() |
| Create custom paths with points | app.create('path', { segments: [...] }) |
| Complex path operations (boolean, offsets) | Paper.js directly |
| Fine-grained path manipulation | Paper.js directly |
| Advanced effects and compound paths | Paper.js directly |
| After Paper.js creation, register for selection | app.itemRegistry.register() |
Core Classes
Point
Represents a 2D point or vector.
// Creation
const p1 = new Point(100, 200);
const p2 = new Point({ x: 100, y: 200 });
const p3 = new Point([100, 200]);
// Operations
const sum = p1.add(p2); // Add points
const diff = p1.subtract(p2); // Subtract points
const scaled = p1.multiply(2); // Scale
const divided = p1.divide(2); // Divide
const normalized = p1.normalize(); // Unit vector
const negated = p1.negate(); // Negate
// Properties
const length = p1.length; // Distance from origin
const angle = p1.angle; // Angle in degrees
const quadrant = p1.quadrant; // 1-4
// Measurements
const distance = p1.getDistance(p2);
const angleTo = p1.getAngle(p2); // Angle between points
// Utilities
const rotated = p1.rotate(45); // Rotate around origin
const rotatedAround = p1.rotate(45, center); // Rotate around point
const rounded = p1.round(); // Round coordinates
const isClose = p1.isClose(p2, 5); // Within tolerance
Size
Represents width and height dimensions.
// Creation
const size = new Size(200, 150);
const size2 = new Size({ width: 200, height: 150 });
// Operations
const scaled = size.multiply(2);
const added = size.add(new Size(50, 50));
// Properties
size.width; // 200
size.height; // 150
Rectangle
Represents a rectangular area.
// Creation methods
const rect1 = new Rectangle(0, 0, 200, 150); // x, y, width, height
const rect2 = new Rectangle(point, size);
const rect3 = new Rectangle(topLeft, bottomRight);
const rect4 = new Rectangle({ point: [0, 0], size: [200, 150] });
// Properties
rect.x, rect.y; // Position
rect.width, rect.height; // Dimensions
rect.point; // Top-left as Point
rect.size; // Size object
rect.center; // Center point
// Corner points
rect.topLeft, rect.topRight;
rect.bottomLeft, rect.bottomRight;
rect.leftCenter, rect.rightCenter;
rect.topCenter, rect.bottomCenter;
// Bounds
rect.left, rect.right; // x bounds
rect.top, rect.bottom; // y bounds
// Methods
rect.contains(point); // Point inside?
rect.contains(otherRect); // Rect inside?
rect.intersects(otherRect); // Overlaps?
rect.intersect(otherRect); // Get intersection
rect.unite(otherRect); // Get union
rect.expand(10); // Expand by amount
rect.scale(2); // Scale from center
Color
Represents colors with multiple format support.
// Creation
const c1 = new Color('#3b82f6'); // Hex
const c2 = new Color(0.2, 0.5, 1); // RGB (0-1)
const c3 = new Color(0.2, 0.5, 1, 0.8); // RGBA
const c4 = new Color('rgb(59, 130, 246)'); // CSS rgb
const c5 = new Color('red'); // Named colors
// HSL/HSB
const hsl = new Color({ hue: 220, saturation: 0.9, lightness: 0.6 });
const hsb = new Color({ hue: 220, saturation: 0.9, brightness: 0.6 });
// Components (all 0-1 range)
color.red, color.green, color.blue;
color.hue; // 0-360
color.saturation; // 0-1
color.brightness; // 0-1
color.lightness; // 0-1
color.alpha; // 0-1
// Operations
const lighter = color.add(0.1); // Lighten
const darker = color.subtract(0.1); // Darken
const blended = color1.blend(color2, 0.5); // Mix colors
// Convert
color.toCSS(); // 'rgb(59, 130, 246)'
color.toCSS(true); // 'rgba(59, 130, 246, 1)'
Path Classes
Path
The fundamental drawing primitive.
// Create empty path
const path = new Path();
// Add segments
path.add(new Point(100, 100));
path.add([150, 50]); // Array shorthand
path.add({ x: 200, y: 100 }); // Object shorthand
// Insert at index
path.insert(1, new Point(125, 75));
// Remove segments
path.removeSegment(1);
path.removeSegments(1, 3); // Remove range
// Close path
path.closed = true;
// Path operations
path.smooth(); // Smooth all segments
path.smooth({ type: 'catmull-rom' });
path.simplify(2.5); // Reduce points (tolerance)
path.flatten(4); // Convert curves to lines
path.reverse(); // Reverse direction
// Get points along path
const point = path.getPointAt(50); // Point at offset
const tangent = path.getTangentAt(50); // Tangent vector
const normal = path.getNormalAt(50); // Normal vector
const curvature = path.getCurvatureAt(50);
// Path properties
path.length; // Total length
path.area; // Enclosed area (closed paths)
path.clockwise; // Winding direction
path.segments; // Array of Segment objects
path.curves; // Array of Curve objects
path.firstSegment;
path.lastSegment;
// Bounds
path.bounds; // Bounding rectangle
path.strokeBounds; // Including stroke
path.handleBounds; // Including handles
// Hit testing
const hit = path.hitTest(point);
const contains = path.contains(point); // Point inside?
const intersections = path.getIntersections(otherPath);
Path Factory Methods
Create common shapes with a single call:
// Circle
const circle = new Path.Circle({
center: [200, 200],
radius: 50,
fillColor: '#3b82f6'
});
// Rectangle
const rect = new Path.Rectangle({
point: [100, 100],
size: [200, 150],
fillColor: '#22c55e'
});
// Rounded rectangle
const rounded = new Path.Rectangle({
point: [100, 100],
size: [200, 100],
radius: 20, // Corner radius
fillColor: '#f59e0b'
});
// Ellipse
const ellipse = new Path.Ellipse({
point: [100, 100],
size: [200, 100],
fillColor: '#8b5cf6'
});
// Line
const line = new Path.Line({
from: [100, 100],
to: [300, 200],
strokeColor: '#ef4444',
strokeWidth: 2
});
// Arc (three-point)
const arc = new Path.Arc({
from: [100, 200],
through: [200, 100],
to: [300, 200],
strokeColor: '#06b6d4'
});
// Regular polygon
const hexagon = new Path.RegularPolygon({
center: [200, 200],
sides: 6,
radius: 50,
fillColor: '#a855f7'
});
// Star
const star = new Path.Star({
center: [200, 200],
points: 5,
radius1: 50, // Outer radius
radius2: 25, // Inner radius
fillColor: '#fbbf24'
});
CompoundPath
Multiple paths as a single item (for holes, complex shapes):
// Create compound path with hole
const outer = new Path.Circle({
center: [200, 200],
radius: 100
});
const inner = new Path.Circle({
center: [200, 200],
radius: 40
});
const donut = new CompoundPath({
children: [outer, inner],
fillColor: '#3b82f6',
fillRule: 'evenodd' // Creates hole
});
// Boolean operations create compound paths
const result = path1.subtract(path2); // Returns CompoundPath
Boolean Operations
Combine paths using set operations:
// Union (combine shapes)
const union = path1.unite(path2);
// Intersection (overlapping area)
const intersection = path1.intersect(path2);
// Subtraction (cut out)
const subtracted = path1.subtract(path2);
// Exclusion (XOR - non-overlapping areas)
const excluded = path1.exclude(path2);
// Divide (split at intersections)
const divided = path1.divide(path2);
// Note: Original paths are not modified
// Results are new Path or CompoundPath objects
Styling Properties
Apply to any path or shape:
// Fill
item.fillColor = '#3b82f6';
item.fillColor = new Color(0.2, 0.5, 1, 0.8); // With alpha
// Stroke
item.strokeColor = '#1e40af';
item.strokeWidth = 3;
item.strokeCap = 'round'; // 'round', 'square', 'butt'
item.strokeJoin = 'round'; // 'round', 'miter', 'bevel'
item.miterLimit = 10; // For miter joins
item.dashArray = [10, 5]; // Dash pattern
item.dashOffset = 0; // Dash phase
// Opacity & Blend
item.opacity = 0.8;
item.blendMode = 'multiply'; // CSS blend modes
// Shadow
item.shadowColor = new Color(0, 0, 0, 0.3);
item.shadowBlur = 10;
item.shadowOffset = new Point(5, 5);
// Selection (for editing)
item.selected = true;
item.fullySelected = true; // Show all handles
Transformations
Modify position, rotation, and scale:
// Position
item.position = new Point(400, 300);
item.position.x += 50;
item.position.y -= 20;
// Bounds-based positioning
item.bounds.center = new Point(400, 300);
item.bounds.topLeft = new Point(0, 0);
// Rotation (degrees)
item.rotation = 45; // Set absolute rotation
item.rotate(30); // Add rotation
item.rotate(30, pivot); // Rotate around point
// Scale
item.scale(2); // Uniform scale
item.scale(2, 0.5); // Non-uniform (x, y)
item.scale(2, pivot); // Scale around point
// Skew/Shear
item.shear(0.5, 0); // Horizontal shear
item.shear(0, 0.5); // Vertical shear
// Matrix transform
item.transform(matrix);
item.matrix; // Get transformation matrix
item.globalMatrix; // Including parent transforms
// Reset
item.rotation = 0;
item.scaling = new Point(1, 1);
Text
Create and style text:
const text = new PointText({
point: [400, 300],
content: 'Hello World',
fontSize: 48,
fontFamily: 'Arial, sans-serif',
fontWeight: 'bold',
fillColor: '#1f2937',
justification: 'center' // 'left', 'center', 'right'
});
// Modify text
text.content = 'Updated Text';
text.fontSize = 64;
// Text bounds
text.bounds; // Bounding rectangle
// Character positioning (read-only)
text.characterStyle; // Default style for new chars
Rasters (Images)
Load and manipulate images:
// Create raster from URL (basic Paper.js way)
const raster = new paper.Raster('https://example.com/image.png');
raster.position = view.center;
raster.onLoad = () => {
console.log('Image loaded:', raster.size);
raster.scale(0.5);
};
// Create raster from data URL
const dataURL = 'data:image/png;base64,...';
const raster2 = new paper.Raster(dataURL);
// Raster properties
raster.size; // Size object
raster.width, raster.height; // Dimensions
raster.resolution; // DPI
// Raster operations
raster.scale(0.5); // Scale
raster.rotate(45); // Rotate
raster.smoothing = true; // Anti-aliasing (default: true)
// Get pixel data
const color = raster.getPixel(x, y); // Color at point
raster.setPixel(x, y, color); // Set pixel color
// Get average color
const avgColor = raster.getAverageColor();
const regionColor = raster.getAverageColor(path); // Within path
PinePaper Image Tools (Recommended)
For MCP tools, use PinePaper’s ImageTools for better integration:
// Upload image to library (stores for reuse)
const entry = await app.imageTools.uploadFromURL('https://example.com/logo.png');
// Place image on canvas (auto-registers with ItemRegistry)
const raster = await app.imageTools.placeImage(entry.id, {
position: [400, 300],
maxWidth: 200
});
// Now the raster is fully integrated:
// - Selectable with click
// - Has registry ID: raster.data.registryId
// - Included in undo/redo
// - Exports with scene
// Apply shape mask
app.imageTools.applyMask(raster, 'circle');
// Apply effects
app.applyEffect(raster, 'sparkle', { color: '#fbbf24' });
// Interactive cropping
app.imageTools.startCrop(raster, { aspectRatio: '1:1' });
app.imageTools.confirmCrop();
Manual Raster Registration
If creating rasters directly with Paper.js, use app.registerItem():
// Create raster
const raster = new paper.Raster('https://example.com/photo.jpg');
await new Promise(resolve => {
raster.onLoad = resolve;
});
// Register with PinePaper (handles layer, data, registry automatically)
const itemId = app.registerItem(raster, 'image', { source: 'external' });
// Now it's interactive
app.historyManager.saveState();
SVG Import
Import and work with SVG graphics:
// Import SVG string (basic Paper.js way)
const svg = paper.project.importSVG(`
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="#3b82f6"/>
</svg>
`, { expandShapes: true });
svg.position = view.center;
svg.scale(2);
// SVG import options
const svg2 = paper.project.importSVG(svgString, {
expandShapes: true, // Convert shapes to paths
insert: false, // Don't auto-add to project
applyMatrix: true // Apply transforms to paths
});
PinePaper SVG Import (Recommended)
// Import with automatic registration
const svg = app.importSVG(`
<svg viewBox="0 0 100 100">
<rect x="10" y="10" width="80" height="80" fill="#22c55e"/>
<circle cx="50" cy="50" r="30" fill="#ffffff"/>
</svg>
`);
// SVG is now:
// - Positioned at center
// - Registered: svg.data.registryId
// - Selected and ready to edit
// - Interactive (hover, drag, etc.)
// Apply animations to imported SVG
app.animate(svg, { animationType: 'pulse', animationSpeed: 0.5 });
// Access SVG children
svg.children.forEach(child => {
console.log(child.className, child.fillColor);
});
Groups
Organize items hierarchically:
// Create group
const group = new Group();
// Add children
group.addChild(circle);
group.addChildren([rect, text]);
// Create with children
const group2 = new Group([circle, rect, text]);
// Access children
group.children; // Array of children
group.firstChild;
group.lastChild;
group.children[0];
// Transform group (affects all children)
group.position = new Point(400, 300);
group.rotate(45);
group.scale(0.5);
// Clipping (mask children to shape)
const clipGroup = new Group({
children: [maskPath, content],
clipped: true // First child becomes mask
});
Layers
Manage drawing order and organization:
// Get layers
const activeLayer = paper.project.activeLayer;
const layers = paper.project.layers;
// Create new layer
const newLayer = new Layer({ name: 'effects' });
// Layer operations
layer.activate(); // Make active for new items
layer.bringToFront();
layer.sendToBack();
layer.insertAbove(otherLayer);
layer.insertBelow(otherLayer);
// Layer visibility
layer.visible = false;
layer.opacity = 0.5;
// PinePaper layers (already created)
// app.backgroundLayer - Background patterns
// app.textLayer - Interactive items
// app.toolLayer - UI overlays
View
Access canvas viewport properties:
const view = paper.view;
// Dimensions
view.size; // Size object
view.size.width;
view.size.height;
view.bounds; // View rectangle
view.center; // Center point
// Zoom and pan
view.zoom = 1.5; // Zoom level
view.center = new Point(500, 300); // Pan to center
// Convert coordinates
view.viewToProject(viewPoint); // Screen to canvas
view.projectToView(canvasPoint); // Canvas to screen
// Force redraw
view.draw();
view.update();
// Animation (use app.addOnFrameCallback for managed callbacks)
view.onFrame = (event) => {
// event.delta - time since last frame (seconds)
// event.time - total elapsed time
// event.count - frame count
};
Project
Access the Paper.js project via app.project or paper.project:
// PinePaper exposes the project directly
const project = app.project;
// Or access via paper namespace
const project = paper.project;
// All items
project.activeLayer;
project.layers;
// Selection
project.selectedItems; // Currently selected items
project.deselectAll();
// Hit testing
const result = project.hitTest(point, {
fill: true,
stroke: true,
segments: true,
tolerance: 5
});
if (result) {
result.item; // Hit item
result.type; // 'fill', 'stroke', 'segment', etc.
result.point; // Exact hit point
result.segment; // If segment hit
}
// Export/Import
const json = project.exportJSON();
const svg = project.exportSVG({ asString: true });
project.importJSON(json);
project.importSVG(svgString);
// Clear
project.clear();
project.activeLayer.removeChildren();
Registering Paper.js Items with PinePaper
After creating items with Paper.js directly, always register them for full PinePaper integration. This enables relations, selection, export, and history.
// Create complex path with Paper.js
const complexShape = new Path();
complexShape.add([100, 100]);
complexShape.arcTo([200, 50], [300, 100]);
complexShape.lineTo([300, 200]);
complexShape.closePath();
complexShape.fillColor = '#3b82f6';
// Register with PinePaper using registerItem()
// This handles: parent layer, item data, cursor, registry
const itemId = app.registerItem(complexShape, 'path', {
customProperty: 'value'
});
// Now the item:
// - Can be selected with click
// - Appears in item registry queries
// - Is included in undo/redo
// - Exports with the scene
// - Can use RELATIONS for animation (see below)
// Save state for undo
app.historyManager.saveState();
registerItem(item, type, properties) - Convenience method that:
- Adds item to the correct layer
- Initializes item data and cursor behavior
- Registers with ItemRegistry
- Returns the registry ID
Animating Paper.js Items with Relations
After registering a Paper.js item, use relations for animation (not direct keyframes). Relations:
- Describe behavior in natural language
- Generate training data for LLM fine-tuning
- Compose together for complex animations
// 1. Create and register items
const planet = new Path.Circle({ center: [400, 300], radius: 30, fillColor: '#3b82f6' });
const sun = new Path.Circle({ center: [400, 300], radius: 60, fillColor: '#fbbf24' });
const planetId = app.registerItem(planet, 'planet');
const sunId = app.registerItem(sun, 'sun');
// 2. Use RELATIONS for animation (RECOMMENDED)
app.addRelation(planetId, sunId, 'orbits', { radius: 150, speed: 0.2 });
// 3. For keyframe animations, use the 'animates' relation
app.addRelation(planetId, planetId, 'animates', {
keyframes: [
{ time: 0, properties: { scale: 1 }, easing: 'easeInOut' },
{ time: 1, properties: { scale: 1.2 }, easing: 'easeInOut' },
{ time: 2, properties: { scale: 1 }, easing: 'easeInOut' }
],
duration: 2,
loop: true
});
// This is BETTER than direct keyframes because:
// - Training data: "planet animates with keyframes over 2 seconds"
// - Natural language description for LLM training
// - Composable with other relations
Why Relations over Keyframes? Relations generate natural language training data. When you use
app.exportRelationTrainingData(), each relation produces instruction/code pairs for fine-tuning. Direct keyframes don’t generate training data.
Complete Example: MCP Tool Pattern
Important: In PinePaper, the API is exposed via
window.PinePaper(orwindow.app). Paper.js classes likePath,Point,Group,PointTextare available globally because Paper.js is installed withpaper.install(window).
// Example: Create a custom badge shape via MCP
// Access PinePaper instance (both are equivalent):
const app = window.PinePaper; // or window.app
function createBadge(params) {
const {
x = 400,
y = 300,
size = 100,
text = 'BADGE',
primaryColor = '#3b82f6',
secondaryColor = '#1e40af'
} = params;
// Create badge shape using Paper.js (classes are global)
const outer = new Path.Circle({
center: [x, y],
radius: size
});
const inner = new Path.Circle({
center: [x, y],
radius: size * 0.85
});
// Create ring using boolean subtraction
const ring = outer.subtract(inner);
ring.fillColor = secondaryColor;
// Clean up source shapes (important!)
outer.remove();
inner.remove();
// Create center circle
const center = new Path.Circle({
center: [x, y],
radius: size * 0.8,
fillColor: primaryColor
});
// Create text
const label = new PointText({
point: [x, y + size * 0.1],
content: text,
fontSize: size * 0.25,
fontFamily: 'Arial Black, sans-serif',
fillColor: '#ffffff',
justification: 'center'
});
// Group all parts
const badge = new Group({
children: [ring, center, label]
});
// Register with PinePaper (handles layer, data, registry)
const itemId = app.registerItem(badge, 'badge', {
text: text,
size: size
});
// Save state for undo
app.historyManager.saveState();
return { badge, itemId };
}
// Usage in MCP tool
const { badge, itemId } = createBadge({
x: 500,
y: 400,
size: 80,
text: 'NEW',
primaryColor: '#ef4444',
secondaryColor: '#b91c1c'
});
// Add animation using RELATIONS (not direct keyframes)
// This generates training data for LLM fine-tuning
app.addRelation(itemId, itemId, 'animates', {
keyframes: [
{ time: 0, properties: { scale: 1 }, easing: 'easeInOut' },
{ time: 0.5, properties: { scale: 1.1 }, easing: 'easeInOut' },
{ time: 1, properties: { scale: 1 }, easing: 'easeInOut' }
],
duration: 1,
loop: true
});
// Or make it orbit another item (if you have another item)
// const targetId = someOtherItem.data.registryId;
// app.addRelation(itemId, targetId, 'orbits', { radius: 100, speed: 0.3 });
Performance Tips
- Batch operations - Wrap multiple changes in
paper.project.activeLayer.removeChildren()+ recreate - Use Groups - Transform groups instead of individual items
- Simplify paths - Use
path.simplify()for complex paths - Visibility over removal - Use
item.visible = falsefor temporary hiding - Avoid onFrame creation - Create items once, update in onFrame
- Use compounds - CompoundPath is faster than many separate paths
See Also
- Core Methods - PinePaper’s simplified API
- Relations Overview - Declarative animation via relationships
- MCP Integration - Build MCP server tools
- MCP Tools Specification - Complete tool definitions
- AI Agents Guide - Agent integration patterns
- Item Registry - Item management
- Paper.js Documentation - Official reference