MCP Integration Guide

This guide covers integrating PinePaper with Model Context Protocol (MCP) servers for AI-powered animation and graphics creation.

Quick Start: Install the official MCP server with npm install -g @pinepaper.studio/mcp-server

See MCP Server Installation for complete setup instructions.

New in v0.3: Use Agent Mode for streamlined MCP workflows. Agent Mode provides quickExport() with smart format detection, platform presets for 16 platforms, and reset() for batch processing.

npm version GitHub

Overview

PinePaper exposes its complete API via window.PinePaper (aliased as app). The three core registries provide the foundation for programmatic control:

Registry Purpose Key Methods
ItemRegistry Track canvas items register, getAll, getByType, query
RelationRegistry Behavior rules & animation addAssociation, getAssociations, exportTrainingData
GeneratorRegistry Background patterns executeGenerator, register, getAll

Animation Philosophy: Relations First

Important: For MCP tools, always prefer relations over direct keyframe animation.

Relations are the declarative, natural language-friendly way to animate in PinePaper:

Approach Description Training Data
Relations (Recommended) Describe behavior: “moon orbits earth” Natural language templates
Keyframes (Low-level) Specify exact property values at times No natural language

Relations internally use keyframes and the animation system, but they:

  1. Are described in natural language (“orbits”, “follows”, “attached_to”)
  2. Generate training data for LLM fine-tuning
  3. Compose together (multiple relations on one item)
  4. Are easier to understand and modify
// RECOMMENDED: Use relations
app.addRelation(moonId, earthId, 'orbits', { radius: 100, speed: 0.5 });

// NOT RECOMMENDED: Direct keyframes (use only when relations don't fit)
app.modify(moon, {
  animationType: 'keyframe',
  keyframes: [...]
});

MCP Tool Categories

Category Tools Description
Item pinepaper_create_item, pinepaper_modify_item, pinepaper_delete_item, pinepaper_batch_create, pinepaper_batch_modify Create and modify canvas items
Relation pinepaper_add_relation, pinepaper_remove_relation, pinepaper_query_relations Behavioral relationships (orbits, follows, attached_to)
Animation pinepaper_animate, pinepaper_keyframe_animate, pinepaper_play_timeline Simple and keyframe animations
Generator pinepaper_execute_generator, pinepaper_list_generators Background generators (sunburst, waves, circuit)
Query pinepaper_query_items, pinepaper_hit_test, pinepaper_layer_info Find and inspect items
Effect pinepaper_apply_effect Visual effects (sparkle, blast)
Export pinepaper_export_svg, pinepaper_export_training_data SVG and training data export
Map pinepaper_load_map, pinepaper_highlight_regions, pinepaper_apply_data_colors, pinepaper_add_marker, pinepaper_export_map, pinepaper_import_custom_map Geographic visualizations

1. Item Creation Tools

Create interactive elements on the canvas.

// Tool: pinepaper_create_item
const item = app.create(type, params);
// Returns: Paper.js item with data.registryId

// Supported types:
// 'text', 'circle', 'star', 'rectangle', 'triangle',
// 'polygon', 'ellipse', 'path', 'line', 'arc'

Item Data Flags:

When creating items, you can set behavior flags via the data parameter:

Flag Type Default Description
selectable boolean true Whether item can be selected by clicking
isDraggable boolean true Whether item can be dragged when selected
isDecorative boolean false Mark as non-interactive decoration (skips selection)
// Create a selectable, draggable planet
const planet = app.create('circle', {
  x: 400, y: 300, radius: 20, color: '#3b82f6',
  data: { selectable: true, isDraggable: true, isPlanet: true }
});

// Create a non-interactive orbit path
const orbit = app.create('circle', {
  x: 400, y: 300, radius: 100,
  strokeColor: 'rgba(255,255,255,0.2)', strokeWidth: 1,
  data: { isDecorative: true, selectable: false }
});

Example MCP Tool Definition:

{
  "name": "pinepaper_create_item",
  "description": "Create an item on the canvas",
  "inputSchema": {
    "type": "object",
    "properties": {
      "type": {
        "type": "string",
        "enum": ["text", "circle", "star", "rectangle", "triangle", "polygon", "ellipse", "path", "line", "arc"],
        "description": "Type of item to create"
      },
      "params": {
        "type": "object",
        "description": "Item-specific parameters (position, size, color, etc.)"
      },
      "data": {
        "type": "object",
        "description": "Item behavior flags (selectable, isDraggable, isDecorative)",
        "properties": {
          "selectable": { "type": "boolean", "description": "Whether item can be selected" },
          "isDraggable": { "type": "boolean", "description": "Whether item can be dragged" },
          "isDecorative": { "type": "boolean", "description": "Mark as non-interactive decoration" }
        }
      }
    },
    "required": ["type", "params"]
  }
}

2. Item Modification Tools

Modify existing items with app.modify().

Sizing: width/height vs scale:

Property Type Description
width number Set item width in pixels (absolute)
height number Set item height in pixels (absolute)
scale number Scale relative to current size (compounds on repeated use)
// RECOMMENDED: Pixel-based sizing for predictable results
app.modify(item, { width: 200, height: 150 });

// Relative scaling (compounds if called multiple times)
app.modify(item, { scale: 1.5 });  // 50% larger each time

Why prefer width/height:

  • width: 200 always results in 200px width
  • scale: 2 doubles current size - calling twice = 4x original
  • Pixel values are predictable and reproducible

Other modifiable properties:

Property Type Description
position [x, y] Set item position
x, y number Set individual coordinates
rotation number Rotation in degrees
opacity number Transparency (0-1)
fillColor string Fill color (hex, rgb, named)
strokeColor string Stroke color
strokeWidth number Stroke width in pixels
fontSize number Font size for text items
content string Text content for text items

3. Animation Tools (Simple Presets)

For quick, preset-based animations:

// Tool: pinepaper_animate
app.animate(item, { animationType, animationSpeed });

// Animation types: pulse, rotate, bounce, fade, wobble, slide, typewriter

Note: For complex animations, use Relations instead (see below). Relations provide natural language descriptions and generate training data.

This is the primary way to add animation in PinePaper. Relations describe behavior in natural language and automatically animate items.

// Tool: pinepaper_add_relation
app.addRelation(sourceId, targetId, relationType, params);

// Core relation types:
// 'orbits'          - Circular motion around target
// 'follows'         - Move toward target with smoothing
// 'attached_to'     - Fixed offset from target
// 'maintains_distance' - Stay fixed distance from target
// 'points_at'       - Rotate to face target
// 'mirrors'         - Mirror position across axis
// 'parallax'        - Move relative to target by depth
// 'bounds_to'       - Stay within target's bounds
// 'animates'        - Keyframe animation (self-referential)

// Manim-inspired animation relations:
// 'grows_from'      - Scale from zero to full size (like GrowFromPoint)
// 'staggered_with'  - Staggered group animation (like LaggedStart)
// 'indicates'       - Temporary highlight pulse (like Indicate)
// 'circumscribes'   - Draw shape around target (like Circumscribe)
// 'wave_through'    - Wave distortion effect (like ApplyWave)
// 'camera_follows'  - View pans to follow target (like MovingCameraScene)
// 'camera_animates' - Keyframe-based camera zoom and pan animation
// 'morphs_to'       - Shape morphing animation (like Transform)

Manim-Inspired Animation Examples:

// Staggered reveal - items appear one after another
items.forEach((item, index) => {
  app.addRelation(item.data.id, null, 'staggered_with', {
    index: index,
    stagger: 0.15,
    effect: 'popIn'
  });
});

// Indicate/highlight an important item
app.addRelation(itemId, null, 'indicates', {
  scale: 1.3,
  color: '#fbbf24',
  duration: 0.6,
  repeat: 2
});

// Circumscribe - draw attention with animated shape
app.addRelation(sourceId, targetId, 'circumscribes', {
  shape: 'circle',
  color: '#ef4444',
  duration: 1.0
});

// Morph one shape into another
app.addRelation(circle.data.id, star.data.id, 'morphs_to', {
  duration: 1.5,
  morphColor: true
});

// Camera follows a moving target
app.addRelation(null, playerId, 'camera_follows', {
  smoothing: 0.1,
  deadzone: 50
});

// Camera keyframe animation - zoom in, pan, zoom out
app.addRelation('camera', null, 'camera_animates', {
  duration: 4,
  loop: true,
  keyframes: [
    { time: 0, zoom: 1, center: [400, 300] },
    { time: 2, zoom: 2, center: [200, 200], easing: 'easeInOut' },
    { time: 4, zoom: 1, center: [400, 300], easing: 'easeInOut' }
  ]
});

// Or use camera helper API
app.camera.zoomIn(2, 0.5);
app.camera.panTo(200, 200, 0.5);
app.camera.moveTo(200, 200, 2, 0.5);
app.camera.reset(0.5);

Example MCP Tool Definition:

{
  "name": "pinepaper_add_relation",
  "description": "Create a behavior relationship between two items. Relations automatically animate items based on their relationship to other items.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "sourceId": {
        "type": "string",
        "description": "Registry ID of the source item (e.g., 'item_1'), use 'camera' for camera_animates, can be null for camera_follows"
      },
      "targetId": {
        "type": "string",
        "description": "Registry ID of the target item, can be null for self-animations"
      },
      "relationType": {
        "type": "string",
        "enum": ["orbits", "follows", "attached_to", "maintains_distance", "points_at", "mirrors", "parallax", "bounds_to", "grows_from", "staggered_with", "indicates", "circumscribes", "wave_through", "camera_follows", "camera_animates", "morphs_to"],
        "description": "Type of relationship"
      },
      "params": {
        "type": "object",
        "description": "Relation-specific parameters"
      }
    },
    "required": ["relationType"]
  }
}

5. Generator Tools

Create procedural backgrounds.

// Tool: pinepaper_execute_generator
await app.executeGenerator(generatorName, params);

// Built-in generators:
// 'drawSunburst', 'drawSunsetScene', 'drawGrid',
// 'drawStackedCircles', 'drawCircuit', 'drawWaves', 'drawPattern'

6. Query Tools

Find and inspect items.

// Tool: pinepaper_query_items (via ItemRegistry)
const items = app.itemRegistry.getAll();
const textItems = app.itemRegistry.getByType('text');
const animated = app.itemRegistry.query(entry => entry.item.data?.animationType);

// Tool: pinepaper_query_items (via Project - Paper.js native query)
const circles = app.queryItems({ data: { shapeType: 'circle' } });
const named = app.queryItems({ name: /^planet/ });
const inBounds = app.queryItems({ inside: new paper.Rectangle(0, 0, 400, 300) });

// Tool: pinepaper_hit_test
const hit = app.hitTest([400, 300]);
const allHits = app.hitTestAll([400, 300]);  // All overlapping items

// Tool: pinepaper_query_relations
const relations = app.getRelations(itemId);
const orbiters = app.queryByRelationTarget(targetId, 'orbits');

// Tool: pinepaper_layer_info
const layers = app.getLayerInfo();

7. Effect Tools

Apply visual effects.

// Tool: pinepaper_apply_effect
app.applyEffect(item, effectType, params);

// Effect types: 'sparkle', 'blast'

8. Export Tools

Export animations and training data.

// Tool: pinepaper_export_svg
const svgString = app.exportAnimatedSVG();

// Tool: pinepaper_export_training_data
const trainingData = app.exportRelationTrainingData();

9. Map Tools

Create data-driven geographic visualizations with the MapSystem.

// Tool: pinepaper_load_map
const mapResult = await app.mapSystem.loadMap(mapId, options);

// Built-in maps:
// 'world'       - World 110m resolution
// 'worldHighRes' - World 50m resolution (recommended for quality)
// 'usa'         - US states map

// Returns: { group, paths, bounds, center }

Map Loading Options:

Option Type Description
projection string Map projection: ‘mercator’, ‘equalEarth’, ‘naturalEarth’, ‘orthographic’, ‘albers’, ‘stereographic’
quality string Rendering quality: ‘fast’, ‘balanced’, ‘professional’ (no simplification)
fillColor string Default fill color for regions
strokeColor string Border color
strokeWidth number Border width
scale number Scale multiplier
center [lon, lat] Center coordinates
rotate [x, y, z] Rotation angles
enableHover boolean Enable hover effects
enableClick boolean Enable click events
hoverFill string Hover fill color
hoverStroke string Hover stroke color

Example MCP Tool Definition:

{
  "name": "pinepaper_load_map",
  "description": "Load a geographic map onto the canvas with specified projection and styling",
  "inputSchema": {
    "type": "object",
    "properties": {
      "mapId": {
        "type": "string",
        "enum": ["world", "worldHighRes", "usa"],
        "description": "Map to load"
      },
      "options": {
        "type": "object",
        "properties": {
          "projection": {
            "type": "string",
            "enum": ["mercator", "equalEarth", "naturalEarth", "orthographic", "albers", "stereographic"],
            "description": "Map projection type"
          },
          "quality": {
            "type": "string",
            "enum": ["fast", "balanced", "professional"],
            "description": "Rendering quality"
          },
          "fillColor": { "type": "string" },
          "strokeColor": { "type": "string" },
          "strokeWidth": { "type": "number" },
          "enableHover": { "type": "boolean" },
          "enableClick": { "type": "boolean" }
        }
      }
    },
    "required": ["mapId"]
  }
}

Region Highlighting:

// Tool: pinepaper_highlight_regions
app.mapSystem.highlightRegions(regionIds, options);

// Region ID formats:
// - World maps: Full country names ("United States of America", "France", "Japan")
// - USA map: State codes ("CA", "TX", "NY")

// Options:
// { fillColor: '#22c55e', strokeColor: '#16a34a', strokeWidth: 2, animate: true }

Example MCP Tool Definition:

{
  "name": "pinepaper_highlight_regions",
  "description": "Highlight specific regions on a loaded map",
  "inputSchema": {
    "type": "object",
    "properties": {
      "regionIds": {
        "type": "array",
        "items": { "type": "string" },
        "description": "Array of region IDs to highlight (country names or state codes)"
      },
      "options": {
        "type": "object",
        "properties": {
          "fillColor": { "type": "string", "description": "Highlight fill color" },
          "strokeColor": { "type": "string", "description": "Highlight stroke color" },
          "strokeWidth": { "type": "number", "description": "Highlight stroke width" },
          "animate": { "type": "boolean", "description": "Animate the highlight" }
        }
      }
    },
    "required": ["regionIds"]
  }
}

Data-Driven Coloring:

// Tool: pinepaper_apply_data_colors
app.mapSystem.applyDataColors(data, options);

// data format: { 'California': 85, 'Texas': 72, 'New York': 90 }
// options: {
//   colorScale: 'blues',     // 'blues', 'greens', 'reds', 'oranges', 'purples', 'heat'
//   minValue: 0,
//   maxValue: 100,
//   showLegend: true,
//   legendPosition: 'bottom-right'
// }

Example MCP Tool Definition:

{
  "name": "pinepaper_apply_data_colors",
  "description": "Apply data-driven coloring to map regions (choropleth)",
  "inputSchema": {
    "type": "object",
    "properties": {
      "data": {
        "type": "object",
        "description": "Object mapping region IDs to numeric values"
      },
      "options": {
        "type": "object",
        "properties": {
          "colorScale": {
            "type": "string",
            "enum": ["blues", "greens", "reds", "oranges", "purples", "heat"],
            "description": "Color scale to use"
          },
          "minValue": { "type": "number" },
          "maxValue": { "type": "number" },
          "showLegend": { "type": "boolean" },
          "legendPosition": {
            "type": "string",
            "enum": ["top-left", "top-right", "bottom-left", "bottom-right"]
          }
        }
      }
    },
    "required": ["data"]
  }
}

Map Markers:

// Tool: pinepaper_add_marker
app.mapSystem.addMarker(options);

// options: {
//   lat: 37.7749,
//   lon: -122.4194,
//   label: 'San Francisco',
//   color: '#ef4444',
//   size: 8,
//   pulse: true
// }

Example MCP Tool Definition:

{
  "name": "pinepaper_add_marker",
  "description": "Add a marker to the map at specified coordinates",
  "inputSchema": {
    "type": "object",
    "properties": {
      "lat": { "type": "number", "description": "Latitude" },
      "lon": { "type": "number", "description": "Longitude" },
      "label": { "type": "string", "description": "Marker label text" },
      "color": { "type": "string", "description": "Marker color" },
      "size": { "type": "number", "description": "Marker size in pixels" },
      "pulse": { "type": "boolean", "description": "Enable pulse animation" }
    },
    "required": ["lat", "lon"]
  }
}

Map Labels:

// Tool: pinepaper_add_map_labels
app.mapSystem.addLabels(options);

// options: {
//   regions: ['California', 'Texas'],  // Or null for all visible
//   fontSize: 10,
//   fontColor: '#374151',
//   labelType: 'name'  // 'name', 'code', 'value'
// }

Map Export:

// Tool: pinepaper_export_map
const mapData = app.mapSystem.exportMap();

// Returns: { mapId, projection, regions, markers, dataColors, ... }

Map Events:

// Maps emit events for interactivity
app.mapSystem.on('regionClick', (event) => {
  console.log('Clicked:', event.regionId, event.properties);
});

app.mapSystem.on('regionHover', (event) => {
  console.log('Hovering:', event.regionId);
});

app.mapSystem.on('markerClick', (event) => {
  console.log('Marker clicked:', event.marker);
});

Custom Map Import:

// Tool: pinepaper_import_custom_map
await app.mapSystem.importCustomMap(geoJson, options);

// Supports GeoJSON and TopoJSON formats
// Useful for detailed country maps from GADM or custom boundaries

Example: Data Visualization Map

// Create a choropleth map showing population data
const mapResult = await app.mapSystem.loadMap('usa', {
  projection: 'albers',
  quality: 'professional',
  fillColor: '#e5e7eb',
  strokeColor: '#9ca3af',
  strokeWidth: 0.5,
  enableHover: true
});

// Apply population data
app.mapSystem.applyDataColors({
  'California': 39538223,
  'Texas': 29145505,
  'Florida': 21538187,
  'New York': 20201249,
  // ... more states
}, {
  colorScale: 'blues',
  showLegend: true,
  legendPosition: 'bottom-right'
});

// Add major city markers
app.mapSystem.addMarker({ lat: 34.0522, lon: -118.2437, label: 'Los Angeles', pulse: true });
app.mapSystem.addMarker({ lat: 40.7128, lon: -74.0060, label: 'New York', pulse: true });

Relation-Based Animation

Relations are the declarative way to create animations. Instead of specifying frame-by-frame movements, you describe relationships and the system animates automatically.

Why Relations for MCP?

  1. Declarative: Describe “what” not “how”
  2. Composable: Multiple relations combine
  3. Training-friendly: Natural language templates

Example: Solar System

// Create celestial bodies
const sun = app.create('circle', { x: 400, y: 300, radius: 50, color: '#fbbf24' });
const earth = app.create('circle', { x: 550, y: 300, radius: 20, color: '#3b82f6' });
const moon = app.create('circle', { x: 580, y: 300, radius: 8, color: '#9ca3af' });

// Get IDs
const sunId = sun.data.registryId;
const earthId = earth.data.registryId;
const moonId = moon.data.registryId;

// Define relationships (animation happens automatically)
app.addRelation(earthId, sunId, 'orbits', { radius: 150, speed: 0.1 });
app.addRelation(moonId, earthId, 'orbits', { radius: 30, speed: 0.3 });

Compositional Relations

Items can have multiple relations:

// Planet orbits AND stays in bounds
app.addRelation(planetId, starId, 'orbits', { radius: 200 });
app.addRelation(planetId, canvasId, 'bounds_to', { padding: 50 });

// Label follows AND points at target
app.addRelation(labelId, targetId, 'follows', { offset: [0, -30] });
app.addRelation(labelId, targetId, 'points_at', { offset_angle: 0 });

Training Data Generation

PinePaper can export relation data for LLM fine-tuning.

Export Format

const trainingData = app.exportRelationTrainingData();

// Output:
[
  {
    instruction: "moon orbits earth at radius 100",
    code: "app.addRelation('item_1', 'item_2', 'orbits', {\"radius\":100})",
    relation: "orbits",
    params: { radius: 100 }
  }
]

Convert to JSONL for Fine-Tuning

const jsonl = trainingData.map(entry => JSON.stringify({
  messages: [
    { role: 'user', content: entry.instruction },
    { role: 'assistant', content: entry.code }
  ]
})).join('\n');

Template Customization

Custom relations can include training templates:

app.relationRegistry.registerRule('bounces_off', {
  description: 'Item bounces off target',
  params: {
    elasticity: { type: 'number', default: 0.8 }
  },
  compute: (ctx) => { /* physics calc */ },
  apply: (item, target, computed, params) => { /* update position */ },
  templates: [
    '{item} bounces off {target}',
    'make {item} bounce off {target}',
    '{item} collides with {target}'
  ]
});

Paper.js Integration

For advanced graphics not covered by app.create(), use Paper.js directly:

// Create complex shape
const path = new paper.Path();
path.add([100, 100]);
path.arcTo([200, 50], [300, 100]);
path.closePath();
path.fillColor = '#3b82f6';

// Register with PinePaper (required for relations, selection, export)
const itemId = app.registerItem(path, 'customPath', { source: 'mcp' });

// Now can use in relations
app.addRelation(itemId, targetId, 'follows', { smoothing: 0.2 });

Boolean Operations

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 = '#8b5cf6';

app.registerItem(ring, 'ring');
outer.remove();
inner.remove();

MCP Server Implementation Example

// Example MCP server tool handler
async function handlePinePaperTool(toolName, args) {
  switch (toolName) {
    case 'create_item':
      const item = app.create(args.type, args.params);
      return { itemId: item.data.registryId, success: true };

    case 'add_relation':
      const success = app.addRelation(
        args.sourceId,
        args.targetId,
        args.relationType,
        args.params || {}
      );
      return { success };

    case 'query_items':
      const items = app.itemRegistry.getAll().map(entry => ({
        id: entry.itemId,
        type: entry.type,
        position: entry.item.position ? {
          x: entry.item.position.x,
          y: entry.item.position.y
        } : null
      }));
      return { items };

    case 'export_training_data':
      const data = app.exportRelationTrainingData();
      return { trainingData: data };

    default:
      throw new Error(`Unknown tool: ${toolName}`);
  }
}

Best Practices for MCP Integration

  1. Use registryId: All items have item.data.registryId for tracking
  2. Prefer relations over keyframes: Relations are more declarative and composable
  3. Save state after batches: Call app.historyManager.saveState() after operations
  4. Check item existence: Use app.itemRegistry.has(id) before operations
  5. Use view.center: Access canvas center with app.view.center
  6. Register Paper.js items: Use app.registerItem() for directly created items
  7. Export training data: Capture examples for model improvement
  8. Use width/height over scale: Pixel values are predictable; scale compounds on repeated calls
  9. Set data flags for interactivity: Use selectable, isDraggable, isDecorative to control item behavior
  10. Mark decorations appropriately: Use isDecorative: true for orbits, guides, or other non-interactive elements

API Quick Reference

Core Methods

Method Description
app.create(type, params) Create item (supports data flags)
app.modify(item, params) Modify item (supports width, height, scale)
app.deleteSelected() Remove selected items
item.remove() Remove specific item (Paper.js)
app.select(query) Select items
app.animate(item, params) Apply simple animation
app.registerItem(item, type, props) Register Paper.js item

Item Data Flags

Flag Default Description
selectable true Can be selected by clicking
isDraggable true Can be dragged when selected
isDecorative false Non-interactive decoration (skips selection)

Relation Methods

Method Description
app.addRelation(from, to, type, params) Add relation
app.removeRelation(from, to, type?) Remove relation
app.getRelations(itemId, type?) Get item’s relations
app.queryByRelationTarget(targetId, type) Find items relating to target
app.getRelationStats() Get relation statistics
app.exportRelationTrainingData() Export training data

Registry Methods

Method Description
app.itemRegistry.getAll() Get all items
app.itemRegistry.getByType(type) Get items by type
app.itemRegistry.query(predicate) Query with function
app.itemRegistry.has(id) Check if item exists
app.generatorRegistry.getAll() Get all generators
app.executeGenerator(name, params) Run generator

Project Query Methods

Method Description
app.queryItems(match) Query items by Paper.js match criteria
app.queryItem(match) Get single item by match
app.hitTest(point, options) Hit test at point
app.hitTestAll(point, options) All items at point
app.isEmpty() Check if canvas has content
app.getLayerInfo() Get layer information

Symbol Methods

Method Description
app.getSymbols() Get all symbol definitions
app.createSymbol(item) Create symbol from item
app.placeSymbol(symbol, pos) Place symbol instance

Export Methods

Method Description
app.exportAnimatedSVG() Export as animated SVG
app.exportTemplate() Export as PinePaper template
app.exportProjectJSON() Export full project state
app.importProjectJSON(json) Import project state
app.exportRelationTrainingData() Export training pairs
app.relationRegistry.exportForSave() Export relations for save

Map Methods

Method Description
app.mapSystem.loadMap(mapId, options) Load geographic map (world, worldHighRes, usa)
app.mapSystem.highlightRegions(ids, options) Highlight specific regions
app.mapSystem.unhighlightRegions(ids) Remove region highlights
app.mapSystem.applyDataColors(data, options) Apply choropleth data coloring
app.mapSystem.addMarker(options) Add marker at lat/lon coordinates
app.mapSystem.addLabels(options) Add region labels
app.mapSystem.getRegionAtPoint(point) Get region at canvas point
app.mapSystem.panTo(lat, lon) Pan map to coordinates
app.mapSystem.zoomTo(level) Set map zoom level
app.mapSystem.exportMap() Export map configuration
app.mapSystem.importCustomMap(geoJson, options) Import custom GeoJSON/TopoJSON
app.mapSystem.on(event, callback) Listen to map events (regionClick, regionHover, markerClick)