Core API Methods
The PinePaper API is exposed globally via window.PinePaper (or app in code examples).
Item Creation
create(type, params)
Create a new item on the canvas.
Parameters:
type(string): Item type -'text','circle','star','rectangle','triangle','polygon','ellipse','path','line','arc'params(object): Item properties
Returns: paper.Item
Basic Shapes
// Text
const text = app.create('text', {
content: 'Hello World',
x: 400,
y: 300,
fontSize: 48,
color: '#4f46e5',
fontFamily: 'Arial, sans-serif'
});
// Circle
const circle = app.create('circle', {
x: 200,
y: 200,
radius: 50,
color: '#ef4444'
});
// Star
const star = app.create('star', {
x: 300,
y: 300,
radius1: 60,
radius2: 30,
color: '#fbbf24'
});
// Rectangle
const rect = app.create('rectangle', {
x: 400,
y: 400,
width: 100,
height: 60,
color: '#22c55e'
});
// Triangle
const tri = app.create('triangle', {
x: 300,
y: 200,
radius: 50,
color: '#f59e0b'
});
// Polygon (hexagon)
const hex = app.create('polygon', {
x: 500,
y: 200,
sides: 6,
radius: 40,
color: '#8b5cf6'
});
// Ellipse
const ellipse = app.create('ellipse', {
x: 400,
y: 300,
width: 120,
height: 60,
color: '#06b6d4'
});
Custom Paths
Create custom paths using segments (array of points) or SVG path data:
// Path from segments (array of [x, y] points)
const customPath = app.create('path', {
segments: [
[100, 100],
[150, 50],
[200, 100],
[250, 150],
[200, 200],
[100, 200]
],
strokeColor: '#3b82f6',
strokeWidth: 3,
closed: true, // Close the path
smooth: true // Smooth the path curves
});
// Path from SVG path data string
const svgPath = app.create('path', {
pathData: 'M 100 100 L 200 100 L 200 200 L 100 200 Z',
fillColor: '#22c55e',
strokeColor: '#15803d',
strokeWidth: 2
});
// Freehand-style path with object points
const freehand = app.create('path', {
segments: [
{ x: 50, y: 100 },
{ x: 100, y: 80 },
{ x: 150, y: 120 },
{ x: 200, y: 90 }
],
strokeColor: '#ef4444',
strokeWidth: 4,
strokeCap: 'round',
strokeJoin: 'round',
smooth: true
});
// Dashed path
const dashed = app.create('path', {
segments: [[100, 100], [300, 100], [300, 200]],
strokeColor: '#6366f1',
strokeWidth: 2,
dashArray: [10, 5] // 10px dash, 5px gap
});
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
segments |
Array | Array of points: [[x, y], ...] or [{x, y}, ...] |
pathData |
string | SVG path data string (e.g., 'M 0 0 L 100 100') |
strokeColor |
string | Stroke color (hex, rgb, named) |
fillColor |
string | Fill color |
strokeWidth |
number | Stroke width in pixels |
strokeCap |
string | 'round', 'square', 'butt' |
strokeJoin |
string | 'round', 'miter', 'bevel' |
dashArray |
Array | Dash pattern [dashLength, gapLength] |
closed |
boolean | Close the path (connect last to first point) |
smooth |
boolean | Smooth the path curves |
simplify |
boolean/number | Simplify path (true or tolerance value) |
Lines and Arcs
// Simple line
const line = app.create('line', {
from: [100, 100],
to: [300, 200],
strokeColor: '#10b981',
strokeWidth: 3,
strokeCap: 'round'
});
// Dashed line
const dashedLine = app.create('line', {
from: [100, 150],
to: [300, 150],
strokeColor: '#f59e0b',
strokeWidth: 2,
dashArray: [15, 5, 5, 5] // Long dash, gap, short dash, gap
});
// Arc (curved line through three points)
const arc = app.create('arc', {
from: [100, 200],
through: [200, 150], // Point the arc passes through
to: [300, 200],
strokeColor: '#8b5cf6',
strokeWidth: 3
});
Item Data Flags
Items can have special data flags that control selection and drag behavior:
// Create an item with custom data flags
const circle = app.create('circle', {
x: 400, y: 300,
radius: 50,
color: '#4f46e5'
});
// Set data flags after creation
circle.data.selectable = true; // Allow selection
circle.data.isDraggable = true; // Allow dragging
circle.data.isDecorative = false; // Not decorative (can be selected)
Available Data Flags:
| Flag | Type | Description |
|---|---|---|
selectable |
boolean | Allow item to be selected (even in background layer) |
isDraggable |
boolean | Allow item to be dragged when selected |
isDecorative |
boolean | Mark as non-interactive (never selectable, e.g., orbit paths) |
Selection Priority:
- Items in
textItemGroup(text layer) are selectable by default - Items in
patternGroup(background) are NOT selectable unlessselectable: true - Items with
isDecorative: trueare never selectable (overrides all) - Items with
selectable: truecan be selected even if in background
Example: Interactive Background Element
// Create a background item that IS selectable
const star = new Path.Star({
center: [400, 300],
points: 5,
radius1: 60,
radius2: 30,
fillColor: '#fbbf24',
parent: app.patternGroup // Normally not selectable
});
// Make it interactive
star.data = {
selectable: true,
isDraggable: true,
customType: 'interactive-star'
};
Example: Decorative (Non-Interactive) Element
// Create a decorative guideline
const guideline = new Path.Line({
from: [0, 300],
to: [800, 300],
strokeColor: 'rgba(100,100,100,0.3)',
strokeWidth: 1
});
// Mark as decorative - will never be selected
guideline.data = { isDecorative: true };
importSVG(svgString)
Import an SVG string into the canvas. The imported SVG is automatically registered with the ItemRegistry and can be selected, animated, and exported.
Parameters:
svgString(string): Valid SVG markup
Returns: paper.Item (Group containing SVG elements)
// Import SVG from string
const svg = app.importSVG(`
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="#3b82f6"/>
<text x="50" y="55" text-anchor="middle" fill="white">Icon</text>
</svg>
`);
// Position and scale
svg.position = app.view.center;
svg.scale(2);
// Apply animation
app.animate(svg, { animationType: 'rotate', animationSpeed: 0.5 });
// Access registry ID
console.log(svg.data.registryId); // 'item_1'
Image Tools
imageTools.uploadFromURL(url)
Upload an image from a URL to the image library.
Parameters:
url(string): Image URL (must support CORS)
Returns: Promise<Object> - Image entry with id, dataURL, width, height
// Upload image from URL
const entry = await app.imageTools.uploadFromURL('https://example.com/image.png');
console.log(entry.id); // 'img_1234567890_abc123'
console.log(entry.width); // Original width
console.log(entry.height); // Original height
imageTools.uploadFromFile(file)
Upload an image from a File object.
Parameters:
file(File): Image file from file input or drag-drop
Returns: Promise<Object> - Image entry
// From file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const entry = await app.imageTools.uploadFromFile(file);
imageTools.placeImage(imageId, options)
Place an image from the library onto the canvas.
Parameters:
imageId(string): Image ID from libraryoptions(object):position(array):[x, y]position (default: center)scale(number): Scale factor (default: 1)maxWidth(number): Maximum width constraintmaxHeight(number): Maximum height constraint
Returns: Promise<paper.Raster> - The placed raster item
// Upload and place image
const entry = await app.imageTools.uploadFromURL('https://example.com/logo.png');
const raster = await app.imageTools.placeImage(entry.id, {
position: [400, 300],
maxWidth: 200,
maxHeight: 200
});
// The raster is automatically registered
console.log(raster.data.registryId); // 'item_2'
// Apply effects
app.applyEffect(raster, 'sparkle', { color: '#fbbf24' });
imageTools.applyMask(raster, shape)
Apply a shape mask to an image.
Parameters:
raster(paper.Raster): The raster item to maskshape(string): Mask shape -'circle','rounded','hexagon','star'
// Apply circular mask
app.imageTools.applyMask(raster, 'circle');
// Apply rounded rectangle mask
app.imageTools.applyMask(raster, 'rounded');
imageTools.startCrop(raster, options)
Start interactive cropping on an image.
Parameters:
raster(paper.Raster): The raster item to cropoptions(object):aspectRatio(string):'free','1:1','16:9','4:3'
// Start cropping with fixed aspect ratio
app.imageTools.startCrop(raster, { aspectRatio: '1:1' });
// Confirm or cancel
app.imageTools.confirmCrop(); // Apply crop
app.imageTools.cancelCrop(); // Cancel
imageTools.getLibrary()
Get all images in the library.
Returns: Array<Object> - Array of image entries
const images = app.imageTools.getLibrary();
images.forEach(entry => {
console.log(entry.id, entry.name, entry.width, entry.height);
});
Paper.js Item Registration
registerItem(item, type, properties)
Register a Paper.js item created directly with PinePaper for full integration. This is the recommended way to add items created with Paper.js directly (for boolean operations, complex paths, etc.).
Parameters:
item(paper.Item): The Paper.js item to registertype(string): Item type name (e.g.,'path','compound','custom')properties(object): Optional custom properties to store
Returns: string - The registry ID for the item
What it does:
- Adds item to the correct layer for interaction
- Initializes item data for selection and drag
- Sets up cursor behavior on hover
- Registers with ItemRegistry for queries
// Create complex shape with Paper.js
const outer = new paper.Path.Circle({ center: [200, 200], radius: 80 });
const inner = new paper.Path.Circle({ center: [200, 200], radius: 40 });
const ring = outer.subtract(inner);
ring.fillColor = '#3b82f6';
// Clean up source shapes
outer.remove();
inner.remove();
// Register with PinePaper
const itemId = app.registerItem(ring, 'ring', { innerRadius: 40, outerRadius: 80 });
// Now the item is fully integrated
app.select(ring, true);
app.animate(ring, { animationType: 'rotate', animationSpeed: 0.5 });
app.historyManager.saveState();
Note: For simple shapes, prefer
app.create()which handles registration automatically.
Selection
select(query, replace)
Select items based on a query.
Parameters:
query: Selection query (see below)replace(boolean): Replace current selection (default: true)
Query Types:
- Item instance: Select specific item
'item_123': Select by ID'name:myItem': Select by name'all': Select all items{ type: 'text' }: Select by filter object
// Select by ID
app.select('item_1');
// Select all text items
app.select({ type: 'text' });
// Add to selection (don't replace)
app.select('item_2', false);
// Select all
app.select('all');
clearSelection()
Deselect all items.
app.clearSelection();
selectedItems
Array of currently selected items.
const items = app.selectedItems;
console.log(`${items.length} items selected`);
Item Modification
modify(item, params)
Modify an existing item’s properties.
Parameters:
item: Item to modify (or use selected items if null)params(object): Properties to change
app.modify(text, {
content: 'Updated Text',
fontSize: 72,
fillColor: '#ef4444'
});
Available Properties:
| Property | Type | Description |
|---|---|---|
x, y |
number | Position in pixels (absolute) |
width |
number | Width in pixels (absolute sizing, recommended) |
height |
number | Height in pixels (absolute sizing, recommended) |
scale |
number | Uniform scale factor (1.0 = 100%, relative) |
scaleX, scaleY |
number | Separate axis scaling |
rotation |
number | Rotation in degrees |
opacity |
number | Opacity (0-1) |
color |
string | Fill color |
strokeColor |
string | Stroke color |
strokeWidth |
number | Stroke width in pixels |
fontSize |
number | Font size (text only) |
content |
string | Text content (text only) |
animationType |
string | Animation type |
animationSpeed |
number | Animation speed multiplier |
Sizing: width/height vs scale
Recommended: Use width and height for predictable sizing.
// ✅ Recommended: Absolute pixel sizing
app.modify(item, { width: 200, height: 150 });
// ⚠️ Relative scaling - compounds on repeated use
app.modify(item, { scale: 1.5 });
| Approach | Behavior | Best For |
|---|---|---|
width/height |
Sets exact pixel dimensions | Precise layouts, design specs |
scale |
Multiplies current size | Proportional adjustments |
Why prefer width/height:
- Absolute values are predictable
- Repeated modifications don’t compound
- Matches standard viewport/CSS sizing
- Easy to specify exact design requirements
Removing Items
Remove items from the canvas using Paper.js directly or by selecting and deleting:
// Remove item directly (Paper.js method)
item.remove();
// Or select and delete
app.select(item);
app.deleteSelected();
// Delete all selected items
app.deleteSelected();
Item Lookup
getItemById(id)
Find item by its registry ID.
const item = app.getItemById('item_1');
getItemsByName(name)
Find items by name.
const items = app.getItemsByName('Logo');
Canvas Operations
setBackgroundColor(color)
Set the canvas background color.
app.setBackgroundColor('#1e293b');
setCanvasSize(width, height, preset)
Change canvas dimensions.
app.setCanvasSize(1920, 1080, 'custom');
// Or use presets
app.setCanvasSize(1080, 1080, 'instagram-square');
resizeCanvas()
Recalculate canvas size to fit container.
app.resizeCanvas();
History
undo()
Undo the last action.
app.historyManager.undo();
redo()
Redo the last undone action.
app.historyManager.redo();
saveState()
Manually save current state to history.
app.historyManager.saveState();
Paper.js Access
view
Access the Paper.js view for canvas dimensions and coordinate information.
const center = app.view.center; // Center point
const size = app.view.viewSize; // View dimensions
const bounds = app.view.bounds; // View rectangle
project
Access the Paper.js project for layers, symbols, and import/export operations.
// Access project layers
const layers = app.project.layers;
const activeLayer = app.project.activeLayer;
// Get all items in the project
const allItems = app.project.getItems({});
// Import SVG into project
const importedItem = app.project.importSVG(svgString);
// Export project as SVG
const svgString = app.project.exportSVG({ asString: true });
// Access symbols
const symbols = app.project.symbols;
// Hit testing at a point
const hitResult = app.project.hitTest(point, {
fill: true,
stroke: true,
tolerance: 5
});
Common Project Methods:
| Method | Description |
|---|---|
project.layers |
Array of all layers |
project.activeLayer |
Currently active layer |
project.getItems(match) |
Find items matching criteria |
project.hitTest(point, options) |
Hit test at a point |
project.importSVG(svg) |
Import SVG content |
project.exportSVG(options) |
Export as SVG |
project.symbols |
All defined symbols |
project.clear() |
Remove all content |
Note: For most operations, prefer using PinePaper’s wrapper methods (below) as they handle registration and PinePaper-specific features automatically.
Project Convenience Methods
PinePaper provides wrapper methods around Paper.js project operations for better integration with the registry system.
queryItems(match)
Query all items in the project matching given criteria.
Parameters:
match(object): Match criteria (see Paper.js match syntax)
Returns: Array<paper.Item>
// Get all circles
const circles = app.queryItems({ data: { shapeType: 'circle' } });
// Get items by name pattern (regex)
const planets = app.queryItems({ name: /^planet/ });
// Get all paths with specific stroke color
const redPaths = app.queryItems({ strokeColor: '#ef4444' });
// Get items in a specific bounds area
const inArea = app.queryItems({ inside: new paper.Rectangle(0, 0, 200, 200) });
queryItem(match)
Get a single item by match criteria.
Parameters:
match(object): Match criteria
Returns: paper.Item|null
// Get item by name
const sun = app.queryItem({ name: 'sun' });
// Get first text item
const firstText = app.queryItem({ class: paper.PointText });
hitTest(point, options)
Perform hit testing at a point to find items.
Parameters:
point(Point|Array): Point to test[x, y]orpaper.Pointoptions(object): Hit test options
Returns: HitResult|null
// Basic hit test
const hit = app.hitTest([400, 300]);
if (hit) {
console.log('Hit:', hit.item.name);
}
// With options
const hit = app.hitTest([400, 300], {
fill: true, // Test fills
stroke: true, // Test strokes
segments: true, // Test path segments
tolerance: 10 // Pixel tolerance
});
hitTestAll(point, options)
Find all items at a point (not just the topmost).
Parameters:
point(Point|Array): Point to testoptions(object): Hit test options
Returns: Array<HitResult>
// Get all items at a point (for overlapping items)
const allHits = app.hitTestAll([400, 300]);
allHits.forEach(hit => {
console.log('Hit:', hit.item.name, 'type:', hit.type);
});
isEmpty()
Check if the project has any content.
Returns: boolean
if (app.isEmpty()) {
console.log('Canvas is empty - show welcome screen');
}
exportProjectJSON(options)
Export entire project state as JSON. This is a low-level export that captures all Paper.js data. For templates, prefer exportTemplate().
Parameters:
options(object): Export options ({ asString: true }default)
Returns: string JSON representation
// Export full project state
const projectJson = app.exportProjectJSON();
// Save to localStorage
localStorage.setItem('pinepaper_backup', projectJson);
importProjectJSON(json)
Import project state from JSON. Warning: This replaces the entire project state.
Parameters:
json(string|object): JSON to import
// Restore from backup
const backup = localStorage.getItem('pinepaper_backup');
if (backup) {
app.importProjectJSON(backup);
}
getSymbols()
Get all symbol definitions in the project. Symbols are efficient for repeated graphics.
Returns: Array<Symbol>
const symbols = app.getSymbols();
symbols.forEach(sym => {
console.log('Symbol:', sym.definition.name);
});
createSymbol(item)
Create a new symbol from an item. The original item becomes the symbol definition.
Parameters:
item(paper.Item): Item to convert to symbol
Returns: Symbol
// Create a reusable star symbol
const star = app.create('star', { x: 100, y: 100, radius1: 30, radius2: 15, color: '#fbbf24' });
const starSymbol = app.createSymbol(star);
// Now place instances efficiently
app.placeSymbol(starSymbol, [200, 200]);
app.placeSymbol(starSymbol, [300, 200]);
app.placeSymbol(starSymbol, [400, 200]);
placeSymbol(symbol, position)
Place a symbol instance on the canvas. Instances share the symbol definition, saving memory.
Parameters:
symbol(Symbol): Symbol to placeposition(Point|Array): Position[x, y]
Returns: SymbolItem
// Create and place multiple instances
const badge = app.createSymbol(badgeGroup);
for (let i = 0; i < 10; i++) {
const placed = app.placeSymbol(badge, [100 + i * 80, 300]);
placed.scale(0.5 + Math.random() * 0.5);
}
getLayerInfo()
Get information about project layers.
Returns: object with layer details
const info = app.getLayerInfo();
console.log('Layer count:', info.count);
console.log('Active layer:', info.active);
info.layers.forEach(layer => {
console.log(`${layer.name}: ${layer.childCount} items, visible: ${layer.visible}`);
});
Project vs Template Export
| Feature | exportProjectJSON() |
exportTemplate() |
|---|---|---|
| Scope | Everything (all layers, styles) | Interactive items + metadata |
| Size | Larger (complete state) | Smaller (optimized) |
| Use case | Backup, full restore | Share, load templates |
| Relations | Not preserved | Preserved |
| Generator params | Not included | Included for recreation |
| Recommended | Internal backups | Sharing & templates |
Layers & Groups
PinePaper uses a layered architecture:
backgroundLayer- Background patterns, drawings, imagestextLayer- Interactive text and shapestoolLayer- UI overlays (selection, handles)
Groups within layers:
patternGroup- Generated patternsdrawingGroup- Freehand drawingsimageGroup- Background imagestextItemGroup- All interactive items
See Also
- Animation - Apply animations to items
- Relations Overview - Behavior-driven animations
- Paper.js Reference - Advanced graphics with Paper.js
- AI Agents Guide - Programmatic API usage
- MCP Integration - Build MCP server tools
- MCP Tools Specification - Complete tool definitions