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:

  1. Items in textItemGroup (text layer) are selectable by default
  2. Items in patternGroup (background) are NOT selectable unless selectable: true
  3. Items with isDecorative: true are never selectable (overrides all)
  4. Items with selectable: true can 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 library
  • options (object):
    • position (array): [x, y] position (default: center)
    • scale (number): Scale factor (default: 1)
    • maxWidth (number): Maximum width constraint
    • maxHeight (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 mask
  • shape (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 crop
  • options (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 register
  • type (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] or paper.Point
  • options (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 test
  • options (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 place
  • position (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, images
  • textLayer - Interactive text and shapes
  • toolLayer - UI overlays (selection, handles)

Groups within layers:

  • patternGroup - Generated patterns
  • drawingGroup - Freehand drawings
  • imageGroup - Background images
  • textItemGroup - All interactive items

See Also