/**
* PinePaper MCP Server
*
* Main server implementation that exposes PinePaper tools via the Model Context Protocol.
*
* This server can operate in two modes:
* 1. Local (stdio): For Claude Desktop and local AI agents
* 2. Cloud (SSE/HTTP): For cloud-hosted premium features (future)
*
* Supports i18n for 41 locales matching PinePaper Studio.
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { PINEPAPER_TOOLS, getLocalizedTools, getToolsForVerbosity } from './tools/definitions.js';
import type { ToolVerbosity } from './tools/definitions.js';
import {
getToolsForToolkit,
detectToolkitFromEnvironment,
detectVerbosityFromEnvironment,
isToolkitExplicitlySet,
isVerbosityExplicitlySet,
getClientProfile,
} from './tools/toolkits.js';
import type { ToolkitProfile } from './tools/toolkits.js';
import { handleToolCall, ExecutionMode, getExecutionMode } from './tools/handlers.js';
import {
I18nManager,
initI18n,
SupportedLocale,
DEFAULT_LOCALE,
} from './i18n/index.js';
import { PROMPTS, getPromptMessages } from './prompts/index.js';
// =============================================================================
// SERVER INFO
// =============================================================================
const SERVER_INFO = {
name: 'pinepaper-mcp',
version: '1.5.1',
description: 'MCP Server for PinePaper Studio - Create animated graphics with AI',
};
// =============================================================================
// RESOURCES (Documentation & Examples)
// =============================================================================
const RESOURCES = [
// === CORE GUIDES (Start here) ===
{
uri: 'pinepaper://docs/getting-started',
name: '🚀 Getting Started',
description: 'START HERE: Tool categories, quick examples, and how to find the right tools',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/templates',
name: '📋 Templates Guide',
description: 'Pre-built templates with animations ready - just change content. Check here first!',
mimeType: 'text/markdown',
},
// === FEATURE GUIDES (How to use each feature) ===
{
uri: 'pinepaper://docs/map-tools',
name: '🗺️ Maps Guide',
description: 'Create USA state maps, world maps, choropleth coloring, and animated map regions',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/diagrams',
name: '📊 Diagrams Guide',
description: 'Create flowcharts, UML diagrams, network diagrams, and auto-layout',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/masks',
name: '🎭 Masks Guide',
description: 'Create reveal effects: wipes, iris, curtains, and custom animated masks',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/relations',
name: '🔄 Animation Guide',
description: 'Animate with relations: orbit, follow, attached, bounce, and more',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/generators',
name: '🎨 Backgrounds Guide',
description: 'Create procedural backgrounds: 14 generators including sunburst, grid, waves, bokeh, gradient mesh, wind field, noise textures',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/effects-and-filters',
name: '✨ Effects Guide',
description: 'Add visual effects: sparkle, blast, filters, and post-processing',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/customization',
name: '🔧 Customization Guide',
description: 'How to adapt templates, create custom designs, and handle any user request',
mimeType: 'text/markdown',
},
// === EXAMPLES (Copy-paste workflows) ===
{
uri: 'pinepaper://examples/us-states-animated',
name: '🇺🇸 US States Animated',
description: 'EXAMPLE: Create a US map with random colors that animate in a loop',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://examples/world-population-map',
name: '🌍 World Population Map',
description: 'EXAMPLE: Choropleth map with population data and color scale',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://examples/flowchart',
name: '📋 Flowchart Example',
description: 'EXAMPLE: Create a decision flowchart with auto-layout',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://examples/solar-system',
name: '🪐 Solar System',
description: 'EXAMPLE: Orbiting planets with relations',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://examples/reveal-animation',
name: '🎬 Reveal Animation',
description: 'EXAMPLE: Text reveal with mask animations',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/fonts',
name: 'Guide: Font Studio',
description: 'Create custom hand-drawn fonts with Font Studio tools',
mimeType: 'text/markdown',
},
// === ADVANCED (For developers) ===
{
uri: 'pinepaper://docs/custom-paths',
name: 'Advanced: Custom Paths',
description: 'SVG paths, Paper.js paths, and path animations',
mimeType: 'text/markdown',
},
{
uri: 'pinepaper://docs/paperjs-in-pinepaper',
name: 'Advanced: Paper.js API',
description: 'Write Paper.js code directly for full control',
mimeType: 'text/markdown',
},
];
const RESOURCE_CONTENTS: Record<string, string> = {
'pinepaper://docs/getting-started': `# Getting Started with PinePaper MCP
## Overview
PinePaper is a canvas-based animation tool with 121 tools for creating animated graphics, custom fonts, maps, diagrams, and more. This guide helps you find the right tools.
## FIRST: Check Templates
PinePaper Studio includes pre-built templates for common use cases. **Templates already have animations configured** - you just change the content!
**Before building from scratch, consider:**
- Is there a template that matches this use case?
- Can I modify an existing template instead of building new?
- Templates save time: just update text, colors, or data
**Use \`pinepaper_create_scene\` for complex animated scenes** - it's 4-8x faster than individual tool calls:
\`\`\`
pinepaper_create_scene
backgroundColor: "#0a0a0a"
items:
- name: "sun", itemType: "circle", position: {x: 400, y: 300}, properties: {radius: 50, color: "#fbbf24"}
- name: "earth", itemType: "circle", position: {x: 550, y: 300}, properties: {radius: 20, color: "#3b82f6"}
relations:
- source: "earth", target: "sun", type: "orbits", params: {radius: 150, speed: 0.5}
animations:
- target: "sun", type: "pulse", speed: 0.3
\`\`\`
## Tool Categories at a Glance
| Category | Use When | Key Tools |
|----------|----------|-----------|
| **Scenes** | Multi-item animated scenes | \`pinepaper_create_scene\` (FASTEST) |
| **Maps** | US states, world maps, choropleth | \`pinepaper_load_map\`, \`pinepaper_apply_data_colors\`, \`pinepaper_animate_map_regions\` |
| **Shapes** | Circles, stars, text, rectangles | \`pinepaper_create_item\` |
| **Animation** | Orbiting, following, pulsing | \`pinepaper_add_relation\`, \`pinepaper_animate\` |
| **Masks** | Reveal effects, wipes, transitions | \`pinepaper_apply_animated_mask\` |
| **Diagrams** | Flowcharts, UML, connections | \`pinepaper_create_diagram_shape\`, \`pinepaper_connect\` |
| **Backgrounds** | Patterns, gradients, generators | \`pinepaper_execute_generator\` |
## Quick Examples
### Create a US Map with Animated Colors
\`\`\`
1. pinepaper_load_map mapId: "usa", options: { projection: "albers" }
2. pinepaper_apply_data_colors data: { "CA": 100, "TX": 80, "NY": 70 }, options: { colorScale: "blues" }
3. pinepaper_animate_map_wave duration: 10, loop: true, colors: ["#ef4444", "#22c55e", "#3b82f6"]
\`\`\`
### Create Orbiting Animation (Using create_scene - Recommended)
\`\`\`
pinepaper_create_scene
items:
- name: "sun", itemType: "circle", position: {x: 400, y: 300}, properties: {radius: 50, color: "#fbbf24"}
- name: "planet", itemType: "circle", position: {x: 550, y: 300}, properties: {radius: 15, color: "#3b82f6"}
relations:
- source: "planet", target: "sun", type: "orbits"
\`\`\`
### Create Reveal Animation
\`\`\`
1. pinepaper_create_item type: "text", params: { x: 400, y: 300, content: "Hello", fontSize: 48 }
2. pinepaper_apply_animated_mask itemId: "item_1", preset: "wipeLeft", options: { duration: 0.5 }
\`\`\`
## Quick Start
Agent mode connects automatically on first tool call. Just start creating!
1. **Check templates first** - existing designs just need content changes
2. **Use \`pinepaper_create_scene\`** for multi-item animated scenes (fastest)
3. **Or create items individually**: \`pinepaper_create_item\` for shapes/text
4. **Add animation**: \`pinepaper_add_relation\` for behavior-based animation
5. **Verify**: \`pinepaper_browser_screenshot\` to see your work
6. **Export**: \`pinepaper_export_svg\` to save
## IMPORTANT: Welcome Template
First-time visitors see a welcome template. Clear it with:
- \`pinepaper_clear_canvas\` - removes all items
- \`pinepaper_agent_reset\` - quick reset
## Key Concepts
### Items
Everything on canvas has a registry ID (e.g., "item_1"). Use this ID for animations and modifications.
### Relations (Animation)
Describe behavior between items:
- \`orbits\`: Circular motion around target
- \`follows\`: Move toward target with smoothing
- \`attached_to\`: Fixed offset from parent
- \`points_at\`: Rotate to face target
### Maps
Built-in maps: \`world\`, \`worldHighRes\`, \`usa\` (US states)
Load with \`pinepaper_load_map\`, animate with \`pinepaper_animate_map_regions\` or \`pinepaper_animate_map_wave\`
### Masks
Reveal effects: wipeLeft, wipeRight, wipeUp, wipeDown, iris, irisOut, star, heart, curtainHorizontal, curtainVertical, cinematic, diagonalWipe, revealUp, revealDown
Apply with \`pinepaper_apply_animated_mask\`
## Agent Workflow (Recommended)
For AI agents, use the batch pipeline for maximum efficiency:
1. \`pinepaper_agent_start_job\` — clear canvas, set preset → returns canvasSize {width, height} for positioning
2. \`pinepaper_agent_batch_execute\` — ONE call with ALL operations (12 types: canvas, background, generators, items, animations, keyframes, relations, masks, effects, playback)
3. \`pinepaper_agent_end_job\` — returns screenshot for user validation
4. User reviews → modify specific items or start new job
See the \`pinepaper_agent_batch_execute\` tool description for full operation reference.
**Note:** Diagrams (flowcharts, UML) use their own tools — \`pinepaper_create_diagram_shape\` + \`pinepaper_connect\` — not batch_execute. See \`pinepaper://docs/diagrams\`.
## Detailed Guides
- Maps: Read \`pinepaper://docs/map-tools\`
- Diagrams: Read \`pinepaper://docs/diagrams\`
- Masks: Read \`pinepaper://docs/masks\`
- Relations: Read \`pinepaper://docs/relations\`
- Generators: Read \`pinepaper://docs/generators\`
- Fonts: Read \`pinepaper://docs/fonts\`
- Customization: Read \`pinepaper://docs/customization\`
`,
'pinepaper://docs/customization': `# Customization Guide
**Intent:** Learn how to handle any user design request by combining tools, adapting templates, and creating custom solutions.
## Understanding User Requests
Users will ask for varied graphics. Here's how to approach any request:
### Step 1: Identify the Category
| Request Type | Primary Tools | Example |
|-------------|---------------|---------|
| Map visualization | \`load_map\`, \`apply_data_colors\` | "Show sales by state" |
| Animated logo | \`create_item\`, \`add_relation\` | "Make my logo pulse" |
| Infographic | \`create_diagram_shape\`, \`connect\` | "Create a flowchart" |
| Social media | \`set_canvas_size\`, \`create_item\` | "Instagram post" |
| Data chart | \`create_item\` (rectangles for bars) | "Bar chart of values" |
| Reveal/intro | \`apply_animated_mask\` | "Text wipe animation" |
### Step 2: Choose Your Approach
| Complexity | Approach | When |
|------------|----------|------|
| Simple | Single tools | One shape, one animation |
| Medium | \`create_scene\` | Multiple items with relations |
| Complex | Step-by-step tools | Custom logic, conditional design |
## Customization Patterns
### Pattern 1: Color Scheme Changes
User: "Make it blue instead of red"
\`\`\`
// Original
pinepaper_create_item itemType: "circle" properties: {color: "#ef4444"}
// Customized - change color property
pinepaper_create_item itemType: "circle" properties: {color: "#3b82f6"}
\`\`\`
**Common color palettes:**
| Theme | Colors |
|-------|--------|
| Professional | #1e293b, #3b82f6, #f8fafc |
| Vibrant | #ef4444, #fbbf24, #22c55e, #3b82f6 |
| Pastel | #fecaca, #fef08a, #bbf7d0, #bfdbfe |
| Dark mode | #0f172a, #1e293b, #334155, #f8fafc |
### Pattern 2: Size and Position Adjustments
User: "Make it bigger" or "Center it"
\`\`\`
// Get canvas size first
pinepaper_get_canvas_size → {width: 800, height: 600}
// Center position = (width/2, height/2)
pinepaper_create_item itemType: "text" position: {x: 400, y: 300}
properties: {content: "Centered", fontSize: 48}
// Make bigger - increase radius, fontSize, width/height
pinepaper_modify_item itemId: "item_1" properties: {scale: 1.5}
\`\`\`
### Pattern 3: Animation Speed/Style Changes
User: "Make it faster" or "Smoother animation"
\`\`\`
// Faster orbit
pinepaper_add_relation sourceId: "item_1" targetId: "item_2" relationType: "orbits"
params: {speed: 2.0} // Higher = faster
// Smoother following
pinepaper_add_relation sourceId: "item_1" targetId: "item_2" relationType: "follows"
params: {smoothing: 0.95} // Higher = smoother (0-1)
// Different easing for masks
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeLeft"
options: {easing: "easeInOut"} // linear, easeIn, easeOut, easeInOut, bounce
\`\`\`
### Pattern 4: Adding Text to Any Design
User: "Add a title" or "Label the items"
\`\`\`
// Title at top
pinepaper_create_item itemType: "text" position: {x: 400, y: 50}
properties: {content: "My Title", fontSize: 36, fontWeight: "bold", color: "#ffffff"}
// Label attached to item (moves with it)
pinepaper_create_item itemType: "text" position: {x: 400, y: 280}
properties: {content: "Earth", fontSize: 14, color: "#94a3b8"}
→ label_1
pinepaper_add_relation sourceId: "label_1" targetId: "earth" relationType: "attached_to"
params: {offset: [0, -30]} // Above the item
\`\`\`
### Pattern 5: Creating Data-Driven Graphics
User: "Show these numbers as a chart"
\`\`\`
// Bar chart from data
// Data: [10, 25, 15, 30, 20]
// Bar 1 (height proportional to value)
pinepaper_create_item itemType: "rectangle" position: {x: 150, y: 350}
properties: {width: 40, height: 100, color: "#3b82f6"} // height = value * scale
// Bar 2
pinepaper_create_item itemType: "rectangle" position: {x: 210, y: 275}
properties: {width: 40, height: 250, color: "#3b82f6"}
// Continue for each data point...
// Position x increases by spacing, height = value * multiplier
\`\`\`
### Pattern 6: Responsive Canvas Sizes
User: "Make it for Instagram" or "YouTube thumbnail"
\`\`\`
// Set canvas FIRST, then position items relative to it
pinepaper_set_canvas_size preset: "instagram-post" // 1080x1080
// Now center = (540, 540)
pinepaper_create_item itemType: "text" position: {x: 540, y: 540}
properties: {content: "Centered!", fontSize: 64}
\`\`\`
**Common presets:**
| Preset | Size | Use |
|--------|------|-----|
| instagram-post | 1080x1080 | Square posts |
| instagram-story | 1080x1920 | Stories, Reels |
| youtube-thumbnail | 1280x720 | Video thumbnails |
| twitter-post | 1200x675 | Twitter/X images |
### Pattern 7: Complex Animations with Multiple Relations
User: "Make planets orbit the sun with moons"
\`\`\`
pinepaper_create_scene
backgroundColor: "#0a0a0a"
items:
- name: "sun", itemType: "circle", position: {x: 400, y: 300}
properties: {radius: 50, color: "#fbbf24"}
- name: "earth", itemType: "circle", position: {x: 550, y: 300}
properties: {radius: 20, color: "#3b82f6"}
- name: "moon", itemType: "circle", position: {x: 580, y: 300}
properties: {radius: 6, color: "#9ca3af"}
- name: "mars", itemType: "circle", position: {x: 650, y: 300}
properties: {radius: 15, color: "#ef4444"}
relations:
- source: "earth", target: "sun", type: "orbits", params: {radius: 150, speed: 0.5}
- source: "moon", target: "earth", type: "orbits", params: {radius: 30, speed: 2}
- source: "mars", target: "sun", type: "orbits", params: {radius: 250, speed: 0.3}
animations:
- target: "sun", type: "pulse", speed: 0.3
\`\`\`
## Handling Edge Cases
### User Provides Specific Colors
Always use their exact hex codes:
\`\`\`
User: "Use #FF5733 for the background"
→ properties: {color: "#FF5733"}
\`\`\`
### User Wants Something Not Directly Supported
Combine tools creatively:
\`\`\`
User: "3D cube effect"
→ Create 3 parallelograms with offset positions and colors
Use paths with segments to draw isometric shapes
\`\`\`
### User Changes Mind Mid-Design
Use modify and delete:
\`\`\`
pinepaper_modify_item itemId: "item_1" properties: {color: "#new_color"}
pinepaper_delete_item itemId: "item_2"
\`\`\`
### Performance with Many Items
Use batch operations:
\`\`\`
pinepaper_batch_create items: [{...}, {...}, {...}] // Instead of multiple create_item calls
\`\`\`
## Decision Tree for Any Request
\`\`\`
Is it a MAP? → pinepaper_load_map
Is it a DIAGRAM/FLOWCHART? → pinepaper_create_diagram_shape + pinepaper_connect
Is it ANIMATED? → pinepaper_add_relation or pinepaper_animate
Is it a REVEAL/INTRO? → pinepaper_apply_animated_mask
Is it DATA VISUALIZATION? → Create shapes proportional to data values
Is it a BACKGROUND? → pinepaper_execute_generator
Otherwise → pinepaper_create_item for shapes/text
\`\`\`
## Example: Complete Custom Request
User: "Create an animated infographic showing 3 steps with arrows"
\`\`\`
// 1. Set canvas
pinepaper_set_canvas_size preset: "presentation-16x9"
// 2. Create step boxes
pinepaper_create_diagram_shape shapeType: "process" position: {x: 300, y: 400}
label: "Step 1: Research" style: {fillColor: "#3b82f6"} → item_1
pinepaper_create_diagram_shape shapeType: "process" position: {x: 600, y: 400}
label: "Step 2: Design" style: {fillColor: "#8b5cf6"} → item_2
pinepaper_create_diagram_shape shapeType: "process" position: {x: 900, y: 400}
label: "Step 3: Build" style: {fillColor: "#22c55e"} → item_3
// 3. Connect with arrows
pinepaper_connect sourceItemId: "item_1" targetItemId: "item_2"
routing: "direct" animated: true
pinepaper_connect sourceItemId: "item_2" targetItemId: "item_3"
routing: "direct" animated: true
// 4. Add title
pinepaper_create_item itemType: "text" position: {x: 600, y: 150}
properties: {content: "Our Process", fontSize: 48, fontWeight: "bold"}
// 5. Apply reveal animations
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeLeft"
options: {startTime: 0, duration: 0.4}
pinepaper_apply_animated_mask itemId: "item_2" preset: "wipeLeft"
options: {startTime: 0.3, duration: 0.4}
pinepaper_apply_animated_mask itemId: "item_3" preset: "wipeLeft"
options: {startTime: 0.6, duration: 0.4}
\`\`\`
## Key Principles
1. **Understand first** - What is the user trying to achieve?
2. **Start simple** - Use the most direct tool for the job
3. **Build incrementally** - Add complexity one step at a time
4. **Verify with screenshots** - \`pinepaper_browser_screenshot\` after each major step
5. **Offer alternatives** - If something can't be done exactly, suggest similar approaches
`,
'pinepaper://docs/templates': `# Templates & Timed Animation Guide
## Apply a Template
\`pinepaper_list_templates\` — browse available pre-built templates.
\`pinepaper_apply_template\` — load a template by name. Animations are pre-configured.
After applying: modify text/colors with \`pinepaper_modify_item\`, then export.
## Template Categories
| Category | Examples |
|----------|----------|
| Map | US Election, World Data, Travel Route |
| Animation | Solar System, Logo Reveal, Text Intro, Particle Burst |
| Diagram | Flowchart, Org Chart, Network |
| Social Media | YouTube Thumbnail (1280x720), Instagram Story (1080x1920), TikTok |
## Modifying Templates
Change safely: \`properties.content\`, \`properties.color\`, \`backgroundColor\`, positions, sizes.
Preserve: \`relations\` array, \`animations\` array, item \`name\` values (relations reference these).
## Building Timed Animations (4 steps)
For invitations, intros, promos, reveals — any timed animation:
**1. Create items** — \`pinepaper_create_scene\` with all items. Timed reveals: set \`opacity: 0\`.
Items get sequential IDs: first = \`item_1\`, second = \`item_2\`, etc.
**2. Animate items** — ONE \`pinepaper_execute_custom_code\` call with all animations:
\`\`\`
app.addAnimation(registryId, keyframes, options)
registryId: 'item_1'
keyframes: [{time: 0, properties: {opacity: 0}, easing: 'easeInOut'},
{time: 1, properties: {opacity: 1}}]
options: {duration: number, loop?: boolean, pingPong?: boolean}
\`\`\`
Animatable properties: \`opacity\`, \`scale\`, \`scaleX\`, \`scaleY\`, \`x\`, \`y\`, \`rotation\`, \`fillColor\`, \`strokeColor\`, \`fontSize\`.
Stagger items by offsetting \`time\` values across items.
**3. Play** — \`pinepaper_play_timeline\`
**4. Export** — \`pinepaper_agent_export\` (SVG/PNG/GIF/MP4/WebM/PDF)
## Text Reveal Pattern
Create text items, then apply mask reveals with staggered start times:
\`\`\`
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeLeft" options: {duration: 0.5}
pinepaper_apply_animated_mask itemId: "item_2" preset: "wipeLeft" options: {startTime: 0.3, duration: 0.5}
\`\`\`
Mask presets: wipeLeft, wipeRight, wipeUp, wipeDown, iris, irisOut, star, heart, curtainHorizontal, curtainVertical, cinematic, diagonalWipe, revealUp, revealDown.
## Advanced Capabilities (via execute_custom_code)
**Time-scoped relations** — relation active only during a time window:
\`\`\`
app.addRelation(sourceId, targetId, type, {startTime: 2, endTime: 5, autoRemove: true, ...params})
\`\`\`
**Keyframe priority** — when keyframes and relations target the same property:
\`\`\`
app.addRelation(sourceId, targetId, type, {relationBehavior: 'keyframeFirst', ...params})
\`\`\`
**Per-item timing** — offset an item into the global timeline:
\`\`\`
app.setTimeOffset(registryId, offsetSeconds)
\`\`\`
**Atomic relation replace** — swap relation params without remove+add:
\`\`\`
app.replaceRelation(sourceId, targetId, type, newParams)
\`\`\`
`,
'pinepaper://docs/relations': `# Relations Guide
Relations are the PRIMARY way to add animation in PinePaper. They describe behavior, not keyframes.
## Why Relations?
1. **Natural Language**: "moon orbits earth" is easier than specifying every frame
2. **Compositional**: Multiple relations can work together
3. **Training Data**: Relations generate instruction/code pairs for LLM fine-tuning
## Available Relations
### orbits
Circular motion around target.
- \`radius\`: Orbit radius in pixels
- \`speed\`: Rotation speed (1 = normal)
- \`direction\`: 'clockwise' or 'counterclockwise'
- \`phase\`: Starting angle offset in degrees
### follows
Move toward target with smoothing.
- \`offset\`: [x, y] offset from target
- \`smoothing\`: 0-1 (lower = more responsive)
- \`delay\`: Seconds of lag
### attached_to
Fixed offset from target (parent-child).
- \`offset\`: [x, y] fixed offset
- \`inherit_rotation\`: Also rotate with parent
### maintains_distance
Stay at fixed distance from target.
- \`distance\`: Target distance in pixels
- \`strength\`: How strongly to maintain (0-1)
### points_at
Rotate to face target.
- \`offset_angle\`: Additional rotation
- \`smoothing\`: Rotation smoothing
### mirrors
Mirror position across axis.
- \`axis\`: 'vertical', 'horizontal', or 'both'
- \`center\`: [x, y] mirror center
### parallax
Move relative to target by depth.
- \`depth\`: 0-1 (0 = no movement, 1 = full movement)
- \`origin\`: [x, y] parallax origin
### bounds_to
Stay within target's bounds.
- \`padding\`: Inner padding
- \`bounce\`: Bounce off edges
### morphs_to
Smoothly transform source shape into target shape over time.
- \`duration\`: Morph duration in seconds (default: 1)
- \`hideTarget\`: Hide target item since it's just a shape reference (default: true)
- \`removeTargetOnComplete\`: Delete target after morph completes (default: false)
- \`easing\`: Timing function ('linear', 'easeIn', 'easeOut', 'easeInOut')
**Usage patterns:**
\`\`\`
// Default: Circle morphs to star's shape, star stays hidden
pinepaper_add_relation sourceId: "circle" targetId: "star" relationType: "morphs_to"
params: {duration: 1}
// Clean up: Delete star after morph completes
pinepaper_add_relation sourceId: "circle" targetId: "star" relationType: "morphs_to"
params: {duration: 1, removeTargetOnComplete: true}
// Both visible: Keep star visible (you'll see two stars at end)
pinepaper_add_relation sourceId: "circle" targetId: "star" relationType: "morphs_to"
params: {duration: 1, hideTarget: false}
\`\`\`
**Great for:** Logo animations, shape transitions, loading indicators, morphing icons
### camera_follows
Camera pans to follow a target item (like Manim MovingCameraScene).
- \`smoothing\`: 0-1 (higher = smoother following)
- \`offset\`: [x, y] offset from target
- \`zoom\`: Zoom level to maintain
- \`deadzone\`: Pixels before camera starts moving
- \`bounds\`: {minX, maxX, minY, maxY} camera movement limits
\`\`\`
// Camera follows the player character
pinepaper_add_relation sourceId: "camera" targetId: "player" relationType: "camera_follows"
params: {smoothing: 0.9, zoom: 1.5, deadzone: 50}
\`\`\`
**Great for:** Game-like scenes, following animations, focus tracking
### camera_animates
Keyframe-based camera zoom and pan animations. Use the special "camera" virtual item.
- \`keyframes\`: Array of keyframe objects with time, zoom, center, easing
- \`duration\`: Total animation duration in seconds
- \`loop\`: Whether to loop the animation
- \`delay\`: Delay before starting
**Keyframe properties:**
- \`time\`: Normalized time 0-1
- \`zoom\`: Zoom level (1 = normal, 2 = 2x zoom in)
- \`center\`: [x, y] pan center position
- \`easing\`: Easing function for this keyframe
\`\`\`
// Cinematic zoom and pan sequence
pinepaper_add_relation sourceId: "camera" targetId: "camera" relationType: "camera_animates"
params:
duration: 5
loop: false
keyframes:
- time: 0, zoom: 1, center: [400, 300], easing: "easeOut"
- time: 0.3, zoom: 1.5, center: [200, 200], easing: "easeInOut"
- time: 0.7, zoom: 2, center: [600, 400], easing: "easeInOut"
- time: 1, zoom: 1, center: [400, 300], easing: "easeIn"
\`\`\`
**Great for:** Cinematic intros, presentations, dramatic reveals, tour animations
## Camera API (Direct Code)
For simpler camera control, use the \`app.camera\` helper API:
\`\`\`javascript
// Zoom
app.camera.zoomIn(1.5, 0.5); // Zoom to 1.5x over 0.5 seconds
app.camera.zoomOut(0.5, 0.5); // Zoom out to 0.5x
// Pan
app.camera.panTo(200, 300, 1); // Pan to coordinates
app.camera.panBy(100, 0, 0.5); // Pan right by 100px
app.camera.panLeft(50, 0.3); // Pan left
app.camera.panRight(50, 0.3); // Pan right
app.camera.panUp(50, 0.3); // Pan up
app.camera.panDown(50, 0.3); // Pan down
// Combined
app.camera.moveTo(200, 300, 2, 1); // Pan to (200,300) and zoom to 2x
// Control
app.camera.reset(0.5); // Reset to default
app.camera.stop(); // Stop current animation
app.camera.getState(); // Returns { zoom, center: [x, y] }
app.camera.isAnimating(); // Check if animating
\`\`\`
## Verifying Animations Are Working
After adding a relation, **always verify** that the animation is actually running:
### Method 1: Take Multiple Screenshots
Take 2-3 screenshots with a small delay between them:
\`\`\`
1. pinepaper_browser_screenshot (save position)
2. Wait 1-2 seconds
3. pinepaper_browser_screenshot (compare position)
\`\`\`
If the orbiting item has moved, the animation is working.
### Method 2: Query Relations
Use \`pinepaper_query_relations\` to confirm the relation was added:
\`\`\`
pinepaper_query_relations itemId=earth direction=outgoing
\`\`\`
Should return the orbits relation.
### Method 3: Check Item Animation State
Use \`pinepaper_get_items\` with filter \`hasRelation: true\` to see animated items.
### Troubleshooting Animation Issues
If animations don't appear to be working:
1. Verify both source and target items exist with \`pinepaper_get_items\`
2. Check the relation was added with \`pinepaper_query_relations\`
3. Try refreshing the page with \`pinepaper_refresh_page\` and recreating
4. For keyframe animations, ensure \`pinepaper_play_timeline\` was called
## Composition Example
An item can have multiple relations:
\`\`\`
// Planet orbits star AND label stays attached
addRelation(planet, star, 'orbits', { radius: 200, speed: 0.5 })
addRelation(label, planet, 'attached_to', { offset: [0, -30] })
\`\`\`
The label will follow the planet as it orbits the star!
`,
'pinepaper://docs/generators': `# Background Generators Guide
**Intent:** Create procedural backgrounds with 14 generators covering patterns, effects, organic flows, particles, and textures.
## Key Tool
\`\`\`
pinepaper_execute_generator
generatorName: "drawSunburst"
params: { rayCount: 16, colors: ["#fbbf24", "#f59e0b"], animated: true }
\`\`\`
## Classic Generators (7)
### drawSunburst
Radial rays from center — energy, attention-grabbing backgrounds.
- **Core:** rayCount (16), colors, bgColor, animated
- **Enhanced:** opacity, rayGap (degrees between rays), gradientRays (radial gradient per ray)
### drawSunsetScene
Animated sunset with clouds — calm, atmospheric.
- **Core:** sunColor, skyColors, cloudCount
- **Enhanced:** skyOpacity, starCount, starColor, reflectionEnabled, reflectionOpacity
### drawGrid
Lines, dots, or squares — clean, technical, modern.
- **Core:** gridType (lines|dots|squares), spacing (40), lineColor, bgColor, lineWidth
- **Enhanced:** opacity, gap (pixel gap), randomRotation, colorMode (checkerboard|gradient|random|rows|columns), strokeColor, strokeWidth
### drawWaves
Layered wave pattern — organic, flowing.
- **Core:** waveCount (5), colors, amplitude (50), frequency (2), bgColor, animated
- **Enhanced:** opacity, fill (fill between waves), fillOpacity, amplitudeVariation, blendMode
### drawCircuit
Circuit board pattern — futuristic, digital.
- **Core:** lineColor, nodeColor, bgColor, density (0-1), animated, boltColor
- **Enhanced:** traceOpacity, nodeOpacity, diagonalPaths (45-degree traces), chipDensity (auto|none|low|medium|high)
### drawStackedCircles
Overlapping circles with distribution algorithms.
- **Core:** count (8), colors, distribution (random|poisson|golden)
- **Enhanced:** opacityMin/opacityMax, blendMode, strokeWidth, strokeColor, sizeGradient, animationType (pulse|float|none)
### drawPattern
Geometric shapes in orbit rings.
- **Core:** patternType, size, color, bgColor
- **Enhanced:** opacity, blendMode, layers (1-5 concentric rings), layerScaleDecay
## New Generators (7)
### drawBokeh (effects)
Soft-focus circles — dreamy, photographic depth-of-field.
\`\`\`
params: { count: 30, colors: ["#f472b6", "#818cf8", "#34d399"], bgColor: "#0f172a",
shadowBlur: 20, distribution: "poisson", driftAnimation: true }
\`\`\`
### drawGradientMesh (organic)
Radial gradient blobs with screen blending — modern, vibrant.
\`\`\`
params: { colors: ["#ec4899", "#8b5cf6", "#06b6d4"], blobCount: 5,
blendMode: "screen", drift: true }
\`\`\`
### drawGeometricAbstract (geometric)
Mixed translucent shapes with blend modes — artistic, contemporary.
\`\`\`
params: { colors: ["#f43f5e", "#3b82f6", "#10b981"], shapeCount: 12,
blendMode: "multiply", rotation: true }
\`\`\`
### drawWindField (particles)
Directional wind particles with noise turbulence — weather, motion.
\`\`\`
params: { particleCount: 200, colors: ["#e0f2fe", "#bae6fd"], direction: 45,
turbulence: 0.5, trailLength: 30, speed: 1.5 }
\`\`\`
### drawFluidFlow (organic)
Sinusoidal stream paths with depth layering — liquid, flowing.
\`\`\`
params: { streamCount: 8, colors: ["#67e8f9", "#a78bfa", "#f9a8d4"],
depthLayers: 3, speed: 0.8 }
\`\`\`
### drawOrganicFlow (organic)
Aurora/silk layers — natural, ethereal glow.
\`\`\`
params: { layerCount: 4, colors: ["#34d399", "#6ee7b7", "#a7f3d0"],
blendMode: "screen", fillToBottom: true, animated: true }
\`\`\`
### drawNoiseTexture (textures)
Perlin/grain/stipple noise — subtle, textured.
\`\`\`
params: { noiseType: "perlin", colors: ["#94a3b8", "#64748b"],
scale: 0.01, density: 0.7, animated: true }
\`\`\`
noiseType options: perlin, grain, stipple
## Quick Mood Guide
| Mood | Generators |
|------|-----------|
| Dreamy/soft | drawBokeh, drawGradientMesh, drawOrganicFlow |
| Techy/modern | drawCircuit, drawGrid, drawWindField |
| Nature/warm | drawSunsetScene, drawSunburst, drawWaves |
| Abstract | drawGeometricAbstract, drawFluidFlow, drawNoiseTexture |
| Decorative | drawPattern, drawStackedCircles |
## Combining Generators with Items
\`\`\`
// Background + content on top
pinepaper_execute_generator
generatorName: "drawBokeh"
params: {count: 20, colors: ["#f472b6", "#818cf8"], shadowBlur: 25}
pinepaper_create_item itemType: "text" position: {x: 400, y: 300}
properties: {content: "Welcome", fontSize: 72, color: "#ffffff"}
\`\`\`
## Best Practices
1. **Call generator first** — before creating items
2. **Use complementary colors** — items should contrast with background
3. **Consider animation performance** — animated generators + many animated items can slow down
4. **Match themes** — sunburst for energy, waves for calm, circuit for tech, bokeh for elegance, noise for texture
5. **Use blendMode** — "screen" brightens, "multiply" darkens, great for layered effects
`,
'pinepaper://docs/custom-paths': `# Custom Paths Guide
Every item in PinePaper is ultimately a Paper.js path. This guide covers advanced path creation techniques including SVG import, custom segments, and path animation.
## Understanding Paths in PinePaper
PinePaper uses Paper.js under the hood. All shapes (circles, stars, rectangles) are paths with specific segment configurations. You can create any shape by defining custom paths.
## Path Creation Methods
### Method 1: Segment-Based Paths (Polygons)
Use the \`path\` item type with \`segments\` to create polygons from coordinate points:
\`\`\`
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
segments: [[0, -50], [50, 50], [-50, 50]] // Triangle
closed: true
fillColor: "#3b82f6"
strokeColor: "#1e40af"
strokeWidth: 2
\`\`\`
Each segment is an [x, y] coordinate relative to the item's position.
**Examples:**
- Triangle: \`[[0, -50], [50, 50], [-50, 50]]\`
- Diamond: \`[[0, -60], [40, 0], [0, 60], [-40, 0]]\`
- Arrow: \`[[0, -40], [30, 20], [10, 20], [10, 60], [-10, 60], [-10, 20], [-30, 20]]\`
- Hexagon: \`[[30, -52], [60, 0], [30, 52], [-30, 52], [-60, 0], [-30, -52]]\`
### Method 2: SVG Path Data (Bezier Curves)
Use \`pathData\` for complex curves using SVG path syntax:
\`\`\`
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
pathData: "M 0 -50 C 50 -50 50 50 0 50 C -50 50 -50 -50 0 -50"
fillColor: "#ec4899"
strokeWidth: 0
\`\`\`
**SVG Path Commands:**
- \`M x y\` - Move to (start point)
- \`L x y\` - Line to
- \`C x1 y1 x2 y2 x y\` - Cubic Bezier curve
- \`Q x1 y1 x y\` - Quadratic Bezier curve
- \`A rx ry rotation large-arc sweep x y\` - Arc
- \`Z\` - Close path
**Example Shapes with pathData:**
\`\`\`
// Heart shape
pathData: "M 0 -30 C -30 -60 -60 -30 -60 0 C -60 30 -30 60 0 80 C 30 60 60 30 60 0 C 60 -30 30 -60 0 -30"
// Smooth wave
pathData: "M -100 0 Q -50 -40 0 0 Q 50 40 100 0"
// Crescent moon
pathData: "M 0 -50 A 50 50 0 1 1 0 50 A 35 35 0 1 0 0 -50"
\`\`\`
### Method 3: Smooth Paths
Add \`smooth: true\` to automatically smooth segment connections:
\`\`\`
pinepaper_create_item
itemType: "path"
properties:
segments: [[0, 0], [50, -30], [100, 0], [150, 30], [200, 0]]
smooth: true
closed: false
strokeColor: "#10b981"
strokeWidth: 3
fillColor: null
\`\`\`
### Method 4: Lines and Arcs
**Simple Line:**
\`\`\`
pinepaper_create_item
itemType: "line"
properties:
from: [100, 100]
to: [300, 200]
strokeColor: "#f59e0b"
strokeWidth: 2
\`\`\`
**Arc (through 3 points):**
\`\`\`
pinepaper_create_item
itemType: "arc"
properties:
from: [100, 300]
through: [200, 200]
to: [300, 300]
strokeColor: "#8b5cf6"
strokeWidth: 3
\`\`\`
## Importing SVGs
### Import SVG from String
\`\`\`
pinepaper_import_svg
svgString: "<svg viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='#ff6b6b'/></svg>"
position: {x: 400, y: 300}
scale: 2.0
\`\`\`
### Import SVG from URL
\`\`\`
pinepaper_import_svg
url: "https://example.com/icon.svg"
position: {x: 400, y: 300}
scale: 1.5
\`\`\`
**Tips for SVG Import:**
- Imported SVGs become Paper.js Group items
- Use \`scale\` to resize (1.0 = original size)
- Position is the center point of the imported SVG
- Complex SVGs with many paths work best when simplified first
## Animating Paths
### Relation-Based Animation (Recommended)
Paths can use all standard relations:
\`\`\`
// Make a custom arrow orbit around a center point
pinepaper_create_item itemType: "path" properties: {segments: [...], closed: true}
→ Returns item_1 (arrow)
pinepaper_create_item itemType: "circle" properties: {radius: 10}
→ Returns item_2 (center)
pinepaper_add_relation
sourceId: "item_1"
targetId: "item_2"
relationType: "orbits"
params: {radius: 100, speed: 0.5}
\`\`\`
### Keyframe Animation for Paths
Animate path properties over time:
\`\`\`
pinepaper_keyframe_animate
itemId: "item_1"
keyframes:
- time: 0
properties: {opacity: 1, scaling: 1}
- time: 1
properties: {opacity: 0.5, scaling: 1.2}
- time: 2
properties: {opacity: 1, scaling: 1}
duration: 2
loop: true
\`\`\`
**Animatable Path Properties:**
- \`opacity\` - Transparency (0-1)
- \`scaling\` - Size multiplier
- \`rotation\` - Rotation in degrees
- \`position.x\`, \`position.y\` - Position
- \`strokeWidth\` - Line thickness
- \`strokeColor\`, \`fillColor\` - Colors (as hex strings)
### Simple Loop Animations
\`\`\`
pinepaper_animate_item
itemId: "item_1"
animationType: "pulse" // or: rotate, bounce, fade, wobble
speed: 1.5
\`\`\`
## Drawing Orbital Rings
Create visible orbital path rings (not just invisible orbit relations):
\`\`\`
// Draw a dashed orbital ring
pinepaper_create_item
itemType: "circle"
position: {x: 400, y: 300}
properties:
radius: 150
fillColor: null // No fill - ring only
strokeColor: "#374151"
strokeWidth: 1
\`\`\`
For elliptical orbits, use an ellipse or path:
\`\`\`
pinepaper_create_item
itemType: "ellipse"
position: {x: 400, y: 300}
properties:
width: 300
height: 150
fillColor: null
strokeColor: "#4b5563"
strokeWidth: 1
\`\`\`
## Creating Complex Shapes
### Compound Paths (Multiple Subpaths)
For shapes with holes, use SVG path data with multiple subpaths:
\`\`\`
// Ring/donut shape (outer circle + inner circle cutout)
pathData: "M 0 -50 A 50 50 0 1 1 0 50 A 50 50 0 1 1 0 -50 M 0 -25 A 25 25 0 1 0 0 25 A 25 25 0 1 0 0 -25"
\`\`\`
### Planetary Surface Details
Create texture details with multiple paths:
\`\`\`
// Planet with crater
1. Main planet: circle with fillColor
2. Crater: smaller ellipse with darker fillColor, attached_to planet
3. Highlight: arc with light strokeColor, attached_to planet
\`\`\`
## Exporting Paths
### Export as Animated SVG
\`\`\`
pinepaper_export_svg
animated: true
\`\`\`
Returns SVG with embedded \`<animate>\` elements for CSS/SMIL animation.
### Export Training Data
\`\`\`
pinepaper_export_training_data
format: "jsonl"
includeMetadata: true
\`\`\`
## Best Practices
1. **Use relative coordinates** in segments - they're relative to item position
2. **Start simple** - Begin with segments, graduate to pathData for curves
3. **Test with screenshots** - Use \`pinepaper_browser_screenshot\` after each path
4. **Combine with relations** - Custom paths animate beautifully with orbits/follows
5. **Layer paths** - Build complex visuals from multiple simple paths
6. **Use smooth: true** - For organic, flowing curves from point data
`,
'pinepaper://examples/solar-system': `# Solar System Example (Advanced)
Create an animated solar system featuring custom paths, orbital rings, and planetary details.
## What You'll Learn
- Drawing orbital ring paths
- Creating planets with surface details using paths
- Using SVG path data for complex shapes
- Combining paths with relation-based animation
- Building layered visual compositions
## Step 1: Canvas Setup
\`\`\`
pinepaper_clear_canvas # Browser auto-connects in agent mode
pinepaper_set_background_color color: "#0a0a1a"
\`\`\`
## Step 2: Create the Sun with Corona Effect
First, create a glowing corona using a custom path:
\`\`\`
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
pathData: "M 0 -80 Q 20 -60 0 -40 Q -20 -60 0 -80 M 57 -57 Q 50 -35 28 -28 Q 35 -50 57 -57 M 80 0 Q 60 20 40 0 Q 60 -20 80 0 M 57 57 Q 35 50 28 28 Q 50 35 57 57 M 0 80 Q -20 60 0 40 Q 20 60 0 80 M -57 57 Q -50 35 -28 28 Q -35 50 -57 57 M -80 0 Q -60 -20 -40 0 Q -60 20 -80 0 M -57 -57 Q -35 -50 -28 -28 Q -50 -35 -57 -57"
strokeColor: "#fbbf24"
strokeWidth: 3
fillColor: null
→ Returns item_1 (corona rays)
\`\`\`
Now create the sun body:
\`\`\`
pinepaper_create_item
itemType: "circle"
position: {x: 400, y: 300}
properties:
radius: 50
color: "#f59e0b"
→ Returns item_2 (sun body)
\`\`\`
Animate the corona:
\`\`\`
pinepaper_animate_item
itemId: "item_1"
animationType: "rotate"
speed: 0.2
\`\`\`
## Step 3: Draw Visible Orbital Rings
Create orbital path rings so users can see the orbits:
\`\`\`
// Mercury's orbit ring
pinepaper_create_item
itemType: "circle"
position: {x: 400, y: 300}
properties:
radius: 90
fillColor: null
strokeColor: "#374151"
strokeWidth: 1
→ Returns item_3 (Mercury orbit ring)
// Earth's orbit ring
pinepaper_create_item
itemType: "circle"
position: {x: 400, y: 300}
properties:
radius: 150
fillColor: null
strokeColor: "#374151"
strokeWidth: 1
→ Returns item_4 (Earth orbit ring)
// Mars's orbit ring
pinepaper_create_item
itemType: "circle"
position: {x: 400, y: 300}
properties:
radius: 210
fillColor: null
strokeColor: "#374151"
strokeWidth: 1
→ Returns item_5 (Mars orbit ring)
\`\`\`
## Step 4: Create Mercury with Surface Detail
\`\`\`
// Mercury body
pinepaper_create_item
itemType: "circle"
position: {x: 490, y: 300}
properties:
radius: 8
color: "#9ca3af"
→ Returns item_6 (Mercury)
// Mercury crater detail (small dark circle)
pinepaper_create_item
itemType: "circle"
position: {x: 492, y: 298}
properties:
radius: 2
color: "#6b7280"
→ Returns item_7 (Mercury crater)
// Attach crater to Mercury
pinepaper_add_relation
sourceId: "item_7"
targetId: "item_6"
relationType: "attached_to"
params: {offset: [2, -2]}
// Mercury orbits the Sun
pinepaper_add_relation
sourceId: "item_6"
targetId: "item_2"
relationType: "orbits"
params: {radius: 90, speed: 1.5, direction: "counterclockwise"}
\`\`\`
## Step 5: Create Earth with Continent Path
\`\`\`
// Earth body
pinepaper_create_item
itemType: "circle"
position: {x: 550, y: 300}
properties:
radius: 15
color: "#3b82f6"
→ Returns item_8 (Earth)
// Continent shape using custom path
pinepaper_create_item
itemType: "path"
position: {x: 550, y: 300}
properties:
segments: [[-5, -8], [3, -10], [8, -5], [6, 2], [0, 5], [-7, 3], [-8, -3]]
closed: true
smooth: true
fillColor: "#22c55e"
strokeWidth: 0
→ Returns item_9 (continent)
// Attach continent to Earth
pinepaper_add_relation
sourceId: "item_9"
targetId: "item_8"
relationType: "attached_to"
params: {offset: [0, 0], inherit_rotation: true}
// Earth orbits the Sun
pinepaper_add_relation
sourceId: "item_8"
targetId: "item_2"
relationType: "orbits"
params: {radius: 150, speed: 0.5, direction: "counterclockwise"}
\`\`\`
## Step 6: Create Moon Orbiting Earth
\`\`\`
pinepaper_create_item
itemType: "circle"
position: {x: 575, y: 300}
properties:
radius: 5
color: "#d1d5db"
→ Returns item_10 (Moon)
// Moon orbits Earth (not the Sun!)
pinepaper_add_relation
sourceId: "item_10"
targetId: "item_8"
relationType: "orbits"
params: {radius: 25, speed: 2.0, direction: "counterclockwise"}
\`\`\`
## Step 7: Create Mars with Polar Ice Cap
\`\`\`
// Mars body
pinepaper_create_item
itemType: "circle"
position: {x: 610, y: 300}
properties:
radius: 12
color: "#dc2626"
→ Returns item_11 (Mars)
// Polar ice cap using arc path
pinepaper_create_item
itemType: "path"
position: {x: 610, y: 300}
properties:
pathData: "M -8 -8 A 12 12 0 0 1 8 -8 L 4 -5 A 8 8 0 0 0 -4 -5 Z"
fillColor: "#fef2f2"
strokeWidth: 0
→ Returns item_12 (ice cap)
// Attach ice cap to Mars
pinepaper_add_relation
sourceId: "item_12"
targetId: "item_11"
relationType: "attached_to"
params: {offset: [0, 0]}
// Mars orbits the Sun
pinepaper_add_relation
sourceId: "item_11"
targetId: "item_2"
relationType: "orbits"
params: {radius: 210, speed: 0.3, direction: "counterclockwise", phase: 45}
\`\`\`
## Step 8: Add Asteroid Belt with Custom Paths
Create a few asteroid shapes:
\`\`\`
// Asteroid 1 - irregular polygon
pinepaper_create_item
itemType: "path"
position: {x: 280, y: 300}
properties:
segments: [[0, -4], [3, -2], [4, 2], [1, 4], [-3, 3], [-4, -1]]
closed: true
fillColor: "#78716c"
strokeWidth: 0
→ Returns item_13
// Asteroid 2
pinepaper_create_item
itemType: "path"
position: {x: 520, y: 300}
properties:
segments: [[0, -3], [4, 0], [2, 4], [-2, 3], [-4, 0]]
closed: true
fillColor: "#a8a29e"
strokeWidth: 0
→ Returns item_14
// Asteroids orbit at different speeds
pinepaper_add_relation sourceId: "item_13" targetId: "item_2" relationType: "orbits"
params: {radius: 270, speed: 0.15, phase: 0}
pinepaper_add_relation sourceId: "item_14" targetId: "item_2" relationType: "orbits"
params: {radius: 275, speed: 0.12, phase: 180}
\`\`\`
## Step 9: Add Comet with Tail Path
\`\`\`
// Comet head
pinepaper_create_item
itemType: "circle"
position: {x: 200, y: 200}
properties:
radius: 6
color: "#67e8f9"
→ Returns item_15 (comet head)
// Comet tail using curved path
pinepaper_create_item
itemType: "path"
position: {x: 200, y: 200}
properties:
pathData: "M 6 0 Q 40 -10 80 -5 Q 120 0 150 10 Q 100 5 50 2 Q 20 0 6 0"
fillColor: "#22d3ee"
opacity: 0.6
strokeWidth: 0
→ Returns item_16 (comet tail)
// Tail follows comet head
pinepaper_add_relation
sourceId: "item_16"
targetId: "item_15"
relationType: "attached_to"
params: {offset: [0, 0]}
// Comet on elliptical-like orbit (using orbits with high speed variation)
pinepaper_add_relation
sourceId: "item_15"
targetId: "item_2"
relationType: "orbits"
params: {radius: 320, speed: 0.8, direction: "clockwise"}
// Comet tail points away from sun
pinepaper_add_relation
sourceId: "item_16"
targetId: "item_2"
relationType: "points_at"
params: {offset_angle: 180}
\`\`\`
## Step 10: Verify and Screenshot
\`\`\`
pinepaper_browser_screenshot
\`\`\`
Take multiple screenshots to verify:
1. All orbital relations are animating
2. Attached items (craters, continents, ice caps) move with their planets
3. The comet tail points away from the sun
## Result
You now have a dynamic solar system featuring:
- **Custom corona rays** around the sun (rotating SVG path)
- **Visible orbital rings** showing each planet's path
- **Surface details** on planets using paths (craters, continents, ice caps)
- **Irregular asteroids** using polygon paths
- **A comet** with a curved tail path that points away from the sun
## Key Techniques Used
| Technique | Example |
|-----------|---------|
| SVG pathData | Corona rays, polar ice cap, comet tail |
| Segment arrays | Continents, asteroids |
| smooth: true | Organic continent shapes |
| attached_to | Details that move with planets |
| orbits | All celestial body motion |
| points_at | Comet tail direction |
| Layered composition | Planet + details as separate items |
## Training Data Generated
This scene produces rich training pairs:
- "Create a continent shape on Earth" → path with segments + attached_to relation
- "Draw the Sun's corona with ray shapes" → SVG pathData with rotate animation
- "Add visible orbit rings" → unfilled circles with stroke only
- "Make comet tail point away from sun" → points_at with offset_angle: 180
`,
'pinepaper://docs/mathematical-patterns': `# Mathematical Patterns Guide
Create stunning visualizations using mathematical formulas. PinePaper excels at bringing math to life through animation.
## Coordinate Systems
PinePaper uses a standard canvas coordinate system:
- **Origin (0, 0)**: Top-left corner
- **X-axis**: Increases rightward
- **Y-axis**: Increases downward
- **Center of 800x600 canvas**: (400, 300)
## Spirals
### Archimedean Spiral
Points move outward at constant rate. Formula: r = a + b*θ
\`\`\`
// Create spiral with 8 points
// Each point: x = cx + r*cos(θ), y = cy + r*sin(θ)
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 410, y: 295} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 405, y: 280} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 385, y: 275} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 365, y: 290} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 360, y: 320} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 380, y: 345} properties: {radius: 4, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 415, y: 355} properties: {radius: 4, color: "#3b82f6"}
\`\`\`
### Logarithmic Spiral (Golden Spiral)
Formula: r = a * e^(b*θ) - Found in nature (shells, galaxies)
\`\`\`
// Connect points with a smooth path
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
segments: [[0, 0], [10, -5], [8, -20], [-10, -28], [-35, -15], [-40, 20], [-20, 50], [25, 60]]
smooth: true
closed: false
strokeColor: "#f59e0b"
strokeWidth: 2
fillColor: null
\`\`\`
## Waves and Oscillations
### Sine Wave
Formula: y = A * sin(ωx + φ)
- A = amplitude (wave height)
- ω = angular frequency (wave density)
- φ = phase shift
\`\`\`
// Create a sine wave using smooth path
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
segments: [
[-200, 0], [-150, -40], [-100, 0], [-50, 40], [0, 0],
[50, -40], [100, 0], [150, 40], [200, 0]
]
smooth: true
closed: false
strokeColor: "#10b981"
strokeWidth: 3
fillColor: null
\`\`\`
### Standing Wave Animation
Two waves traveling in opposite directions:
\`\`\`
// Create two wave paths, animate in opposite directions
pinepaper_create_item itemType: "path" position: {x: 400, y: 280}
properties: {segments: [[-200, 0], [-100, -30], [0, 0], [100, 30], [200, 0]], smooth: true, strokeColor: "#3b82f6", strokeWidth: 2}
→ item_1
pinepaper_create_item itemType: "path" position: {x: 400, y: 320}
properties: {segments: [[-200, 0], [-100, 30], [0, 0], [100, -30], [200, 0]], smooth: true, strokeColor: "#ef4444", strokeWidth: 2}
→ item_2
pinepaper_animate_item itemId: "item_1" animationType: "slide" speed: 0.5
pinepaper_animate_item itemId: "item_2" animationType: "slide" speed: -0.5
\`\`\`
## Geometric Patterns
### Regular Polygons
Formula for vertices: x = r*cos(2πk/n), y = r*sin(2πk/n)
**Pentagon (5 sides):**
\`\`\`
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 5, radius: 80, color: "#8b5cf6"}
\`\`\`
**Hexagon (6 sides):**
\`\`\`
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 6, radius: 80, color: "#06b6d4"}
\`\`\`
**Octagon (8 sides):**
\`\`\`
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 8, radius: 80, color: "#f43f5e"}
\`\`\`
### Nested Polygons (Mandala Pattern)
\`\`\`
// Create concentric rotating polygons
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 6, radius: 120, color: null, strokeColor: "#3b82f6", strokeWidth: 2}
→ item_1
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 6, radius: 90, color: null, strokeColor: "#8b5cf6", strokeWidth: 2}
→ item_2
pinepaper_create_item itemType: "polygon" position: {x: 400, y: 300}
properties: {sides: 6, radius: 60, color: null, strokeColor: "#ec4899", strokeWidth: 2}
→ item_3
// Rotate at different speeds
pinepaper_animate_item itemId: "item_1" animationType: "rotate" speed: 0.2
pinepaper_animate_item itemId: "item_2" animationType: "rotate" speed: -0.3
pinepaper_animate_item itemId: "item_3" animationType: "rotate" speed: 0.5
\`\`\`
## Stars and Stellations
### Star Geometry
Stars are defined by two radii: outer (points) and inner (valleys)
- **Sharp stars**: inner radius << outer radius
- **Thick stars**: inner radius close to outer radius
\`\`\`
// Sharp 5-pointed star
pinepaper_create_item itemType: "star" position: {x: 300, y: 300}
properties: {points: 5, radius1: 80, radius2: 30, color: "#fbbf24"}
// Thick 5-pointed star
pinepaper_create_item itemType: "star" position: {x: 500, y: 300}
properties: {points: 5, radius1: 80, radius2: 55, color: "#f59e0b"}
\`\`\`
### Star Variations
\`\`\`
// 4-pointed star (diamond burst)
pinepaper_create_item itemType: "star" properties: {points: 4, radius1: 60, radius2: 15, color: "#ef4444"}
// 8-pointed star (compass rose)
pinepaper_create_item itemType: "star" properties: {points: 8, radius1: 70, radius2: 35, color: "#3b82f6"}
// 12-pointed star (sun burst)
pinepaper_create_item itemType: "star" properties: {points: 12, radius1: 80, radius2: 60, color: "#fbbf24"}
\`\`\`
## Lissajous Figures
Curves from parametric equations: x = A*sin(at + δ), y = B*sin(bt)
These create beautiful interlocking patterns based on frequency ratios:
\`\`\`
// 1:2 Lissajous (figure-8 pattern)
pinepaper_create_item
itemType: "path"
position: {x: 400, y: 300}
properties:
pathData: "M 0 -60 C 60 -60 60 0 0 0 C -60 0 -60 60 0 60 C 60 60 60 0 0 0 C -60 0 -60 -60 0 -60"
strokeColor: "#10b981"
strokeWidth: 2
fillColor: null
\`\`\`
## Circular Arrangements
### Items Around a Circle
Place n items evenly around a circle of radius r:
- angle = 2π * i / n
- x = center_x + r * cos(angle)
- y = center_y + r * sin(angle)
\`\`\`
// 8 circles arranged in a ring
pinepaper_create_item itemType: "circle" position: {x: 480, y: 300} properties: {radius: 15, color: "#ef4444"}
pinepaper_create_item itemType: "circle" position: {x: 457, y: 357} properties: {radius: 15, color: "#f59e0b"}
pinepaper_create_item itemType: "circle" position: {x: 400, y: 380} properties: {radius: 15, color: "#eab308"}
pinepaper_create_item itemType: "circle" position: {x: 343, y: 357} properties: {radius: 15, color: "#22c55e"}
pinepaper_create_item itemType: "circle" position: {x: 320, y: 300} properties: {radius: 15, color: "#06b6d4"}
pinepaper_create_item itemType: "circle" position: {x: 343, y: 243} properties: {radius: 15, color: "#3b82f6"}
pinepaper_create_item itemType: "circle" position: {x: 400, y: 220} properties: {radius: 15, color: "#8b5cf6"}
pinepaper_create_item itemType: "circle" position: {x: 457, y: 243} properties: {radius: 15, color: "#ec4899"}
\`\`\`
### Animated Circular Arrangement
\`\`\`
// Create center anchor
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 5, color: "#ffffff"}
→ item_center
// Create orbiting items with different phases
pinepaper_create_item itemType: "circle" position: {x: 480, y: 300} properties: {radius: 12, color: "#ef4444"}
→ item_1
pinepaper_add_relation sourceId: "item_1" targetId: "item_center" relationType: "orbits"
params: {radius: 80, speed: 0.5, phase: 0}
pinepaper_create_item itemType: "circle" position: {x: 400, y: 380} properties: {radius: 12, color: "#22c55e"}
→ item_2
pinepaper_add_relation sourceId: "item_2" targetId: "item_center" relationType: "orbits"
params: {radius: 80, speed: 0.5, phase: 90}
pinepaper_create_item itemType: "circle" position: {x: 320, y: 300} properties: {radius: 12, color: "#3b82f6"}
→ item_3
pinepaper_add_relation sourceId: "item_3" targetId: "item_center" relationType: "orbits"
params: {radius: 80, speed: 0.5, phase: 180}
pinepaper_create_item itemType: "circle" position: {x: 400, y: 220} properties: {radius: 12, color: "#f59e0b"}
→ item_4
pinepaper_add_relation sourceId: "item_4" targetId: "item_center" relationType: "orbits"
params: {radius: 80, speed: 0.5, phase: 270}
\`\`\`
## Fractals and Self-Similarity
### Simple Fractal Tree (3 levels)
\`\`\`
// Trunk
pinepaper_create_item itemType: "line" properties: {from: [400, 400], to: [400, 300], strokeColor: "#78350f", strokeWidth: 8}
// Level 1 branches
pinepaper_create_item itemType: "line" properties: {from: [400, 300], to: [350, 230], strokeColor: "#78350f", strokeWidth: 5}
pinepaper_create_item itemType: "line" properties: {from: [400, 300], to: [450, 230], strokeColor: "#78350f", strokeWidth: 5}
// Level 2 branches (left)
pinepaper_create_item itemType: "line" properties: {from: [350, 230], to: [320, 180], strokeColor: "#166534", strokeWidth: 3}
pinepaper_create_item itemType: "line" properties: {from: [350, 230], to: [370, 175], strokeColor: "#166534", strokeWidth: 3}
// Level 2 branches (right)
pinepaper_create_item itemType: "line" properties: {from: [450, 230], to: [430, 175], strokeColor: "#166534", strokeWidth: 3}
pinepaper_create_item itemType: "line" properties: {from: [450, 230], to: [480, 180], strokeColor: "#166534", strokeWidth: 3}
\`\`\`
### Sierpinski Triangle Pattern
\`\`\`
// Main triangle
pinepaper_create_item itemType: "path" position: {x: 400, y: 320}
properties: {segments: [[0, -100], [87, 50], [-87, 50]], closed: true, strokeColor: "#8b5cf6", strokeWidth: 2, fillColor: null}
// Inner inverted triangle (creates the hole)
pinepaper_create_item itemType: "path" position: {x: 400, y: 320}
properties: {segments: [[0, 0], [43, -50], [-43, -50]], closed: true, fillColor: "#1a1a2e", strokeWidth: 0}
\`\`\`
## Physics Simulations
### Pendulum Motion
\`\`\`
// Fixed pivot point
pinepaper_create_item itemType: "circle" position: {x: 400, y: 150} properties: {radius: 8, color: "#6b7280"}
→ pivot
// Pendulum bob
pinepaper_create_item itemType: "circle" position: {x: 400, y: 350} properties: {radius: 25, color: "#3b82f6"}
→ bob
// Use wobble animation to simulate pendulum swing
pinepaper_animate_item itemId: "bob" animationType: "wobble" speed: 0.8
\`\`\`
### Orbital Mechanics (Kepler's Laws)
\`\`\`
// Central body (star)
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 40, color: "#fbbf24"}
→ star
// Inner planet (faster orbit - Kepler's 3rd law)
pinepaper_create_item itemType: "circle" position: {x: 480, y: 300} properties: {radius: 10, color: "#ef4444"}
→ inner
pinepaper_add_relation sourceId: "inner" targetId: "star" relationType: "orbits"
params: {radius: 80, speed: 2.0}
// Outer planet (slower orbit)
pinepaper_create_item itemType: "circle" position: {x: 560, y: 300} properties: {radius: 15, color: "#3b82f6"}
→ outer
pinepaper_add_relation sourceId: "outer" targetId: "star" relationType: "orbits"
params: {radius: 160, speed: 0.7}
\`\`\`
## Trigonometric Visualizations
### Unit Circle
\`\`\`
// Circle
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 100, fillColor: null, strokeColor: "#4b5563", strokeWidth: 2}
// X-axis
pinepaper_create_item itemType: "line" properties: {from: [280, 300], to: [520, 300], strokeColor: "#6b7280", strokeWidth: 1}
// Y-axis
pinepaper_create_item itemType: "line" properties: {from: [400, 180], to: [400, 420], strokeColor: "#6b7280", strokeWidth: 1}
// Rotating radius line
pinepaper_create_item itemType: "line" properties: {from: [400, 300], to: [500, 300], strokeColor: "#ef4444", strokeWidth: 2}
→ radius
pinepaper_animate_item itemId: "radius" animationType: "rotate" speed: 0.3
// Point on circle
pinepaper_create_item itemType: "circle" position: {x: 500, y: 300} properties: {radius: 6, color: "#ef4444"}
→ point
pinepaper_add_relation sourceId: "point" targetId: "radius" relationType: "attached_to"
params: {offset: [100, 0], inherit_rotation: true}
\`\`\`
## Color Mathematics
### Color Wheel
\`\`\`
// 12 hues arranged in a circle (30° apart)
// Hue values: 0°=red, 60°=yellow, 120°=green, 180°=cyan, 240°=blue, 300°=magenta
pinepaper_create_item itemType: "circle" position: {x: 480, y: 300} properties: {radius: 20, color: "#ef4444"} // 0° Red
pinepaper_create_item itemType: "circle" position: {x: 457, y: 357} properties: {radius: 20, color: "#f97316"} // 30° Orange
pinepaper_create_item itemType: "circle" position: {x: 400, y: 380} properties: {radius: 20, color: "#eab308"} // 60° Yellow
pinepaper_create_item itemType: "circle" position: {x: 343, y: 357} properties: {radius: 20, color: "#84cc16"} // 90° Yellow-Green
pinepaper_create_item itemType: "circle" position: {x: 320, y: 300} properties: {radius: 20, color: "#22c55e"} // 120° Green
pinepaper_create_item itemType: "circle" position: {x: 343, y: 243} properties: {radius: 20, color: "#14b8a6"} // 150° Teal
pinepaper_create_item itemType: "circle" position: {x: 400, y: 220} properties: {radius: 20, color: "#06b6d4"} // 180° Cyan
pinepaper_create_item itemType: "circle" position: {x: 457, y: 243} properties: {radius: 20, color: "#3b82f6"} // 210° Blue
\`\`\`
## Formulas Quick Reference
| Pattern | Formula | PinePaper Approach |
|---------|---------|-------------------|
| Circle point | (r*cos θ, r*sin θ) | Use orbits relation |
| Spiral | r = a + bθ | Create points, connect with smooth path |
| Sine wave | y = A*sin(ωx) | Path with smooth segments |
| Regular polygon | 2π/n angle between vertices | Use polygon itemType with sides param |
| Golden ratio | φ = 1.618... | radius2 = radius1 / 1.618 for stars |
| Fibonacci | 1, 1, 2, 3, 5, 8... | Size sequence for nested shapes |
`,
'pinepaper://docs/effects-and-filters': `# Effects & Filters Guide
Complete reference for visual effects, particle effects, and post-processing filters in PinePaper.
## Particle Effects
### Sparkle Effect
Adds glittering particles around an item.
\`\`\`
pinepaper_apply_effect
itemId: "item_1"
effectType: "sparkle"
params:
color: "#fbbf24" // Sparkle color (default: gold)
speed: 1.5 // Animation speed multiplier
size: 4 // Particle size in pixels
\`\`\`
**Use cases:**
- Magic wand effects
- Highlight important items
- Create fairy dust trails
- Award/achievement animations
### Blast Effect
Creates an explosion/burst of particles from an item.
\`\`\`
pinepaper_apply_effect
itemId: "item_1"
effectType: "blast"
params:
color: "#ef4444" // Particle color (default: red)
radius: 150 // How far particles travel
count: 30 // Number of particles
\`\`\`
**Use cases:**
- Explosions
- Confetti celebrations
- Impact effects
- Dramatic reveals
## Canvas Filters
Filters apply to the entire canvas and can be stacked for combined effects.
### Color Adjustments
**Grayscale** - Remove color saturation
\`\`\`
pinepaper_add_filter filterType: "grayscale" params: {intensity: 1.0}
// intensity: 0 (no effect) to 1 (full grayscale)
\`\`\`
**Sepia** - Warm vintage brown tones
\`\`\`
pinepaper_add_filter filterType: "sepia" params: {intensity: 0.8}
\`\`\`
**Saturation** - Boost or reduce color intensity
\`\`\`
pinepaper_add_filter filterType: "saturate" params: {value: 50}
// value: -100 (desaturated) to 100 (oversaturated)
\`\`\`
**Brightness** - Lighten or darken
\`\`\`
pinepaper_add_filter filterType: "brightness" params: {value: 20}
// value: -100 (black) to 100 (white)
\`\`\`
**Contrast** - Increase or decrease contrast
\`\`\`
pinepaper_add_filter filterType: "contrast" params: {value: 30}
// value: -100 (flat gray) to 100 (high contrast)
\`\`\`
**Invert** - Invert all colors
\`\`\`
pinepaper_add_filter filterType: "invert" params: {intensity: 1.0}
\`\`\`
**Color Overlay** - Tint with a color
\`\`\`
pinepaper_add_filter filterType: "colorOverlay" params: {
color: "#3b82f6",
intensity: 0.3
}
\`\`\`
### Blur & Sharpness
**Blur** - Gaussian blur effect
\`\`\`
pinepaper_add_filter filterType: "blur" params: {radius: 5}
// radius: 0 (sharp) to 20 (very blurry)
\`\`\`
**Sharpen** - Increase edge definition
\`\`\`
pinepaper_add_filter filterType: "sharpen" params: {intensity: 50}
// intensity: 0 to 100
\`\`\`
### Artistic Effects
**Noise/Grain** - Film grain texture
\`\`\`
pinepaper_add_filter filterType: "noise" params: {
intensity: 30, // Grain amount (0-100)
monochrome: true // Black and white grain
}
\`\`\`
**Vignette** - Darken edges
\`\`\`
pinepaper_add_filter filterType: "vignette" params: {
intensity: 0.5, // Darkness (0-1)
radius: 0.7 // How far from edge (0-1)
}
\`\`\`
**Emboss** - 3D relief effect
\`\`\`
pinepaper_add_filter filterType: "emboss" params: {}
\`\`\`
**Posterize** - Reduce color levels
\`\`\`
pinepaper_add_filter filterType: "posterize" params: {levels: 4}
// levels: 2 (extreme) to 32 (subtle)
\`\`\`
### Preset Combinations
**Vintage** - Classic old photo look
\`\`\`
pinepaper_add_filter filterType: "vintage" params: {intensity: 0.7}
// Combines sepia, vignette, and noise
\`\`\`
## Filter Stacking Examples
### Cinematic Look
\`\`\`
pinepaper_add_filter filterType: "contrast" params: {value: 15}
pinepaper_add_filter filterType: "vignette" params: {intensity: 0.4, radius: 0.8}
pinepaper_add_filter filterType: "colorOverlay" params: {color: "#1e3a5f", intensity: 0.15}
\`\`\`
### Dreamy/Soft
\`\`\`
pinepaper_add_filter filterType: "blur" params: {radius: 2}
pinepaper_add_filter filterType: "brightness" params: {value: 10}
pinepaper_add_filter filterType: "saturate" params: {value: -20}
\`\`\`
### Retro/80s
\`\`\`
pinepaper_add_filter filterType: "contrast" params: {value: 20}
pinepaper_add_filter filterType: "saturate" params: {value: 40}
pinepaper_add_filter filterType: "noise" params: {intensity: 15, monochrome: false}
\`\`\`
### Horror/Dark
\`\`\`
pinepaper_add_filter filterType: "contrast" params: {value: 40}
pinepaper_add_filter filterType: "saturate" params: {value: -50}
pinepaper_add_filter filterType: "vignette" params: {intensity: 0.8, radius: 0.5}
\`\`\`
### Old Film
\`\`\`
pinepaper_add_filter filterType: "sepia" params: {intensity: 0.6}
pinepaper_add_filter filterType: "noise" params: {intensity: 40, monochrome: true}
pinepaper_add_filter filterType: "vignette" params: {intensity: 0.5}
pinepaper_add_filter filterType: "contrast" params: {value: 10}
\`\`\`
## Combining Effects with Animation
### Glowing Orb
\`\`\`
// Create orb
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 40, color: "#8b5cf6"}
→ item_1
// Add pulse animation
pinepaper_animate_item itemId: "item_1" animationType: "pulse" speed: 0.8
// Add sparkle effect
pinepaper_apply_effect itemId: "item_1" effectType: "sparkle"
params: {color: "#c4b5fd", speed: 1.2, size: 3}
\`\`\`
### Explosion Sequence
\`\`\`
// Create target
pinepaper_create_item itemType: "star" position: {x: 400, y: 300}
properties: {points: 5, radius1: 50, radius2: 20, color: "#ef4444"}
→ item_1
// Add blast effect
pinepaper_apply_effect itemId: "item_1" effectType: "blast"
params: {color: "#fbbf24", radius: 200, count: 40}
\`\`\`
## Effect Parameters Reference
| Effect | Parameter | Range | Default | Description |
|--------|-----------|-------|---------|-------------|
| sparkle | color | hex/rgb | #fbbf24 | Particle color |
| sparkle | speed | 0.1-5 | 1 | Animation speed |
| sparkle | size | 1-20 | 3 | Particle size |
| blast | color | hex/rgb | #ef4444 | Particle color |
| blast | radius | 10-500 | 100 | Explosion radius |
| blast | count | 5-100 | 20 | Particle count |
## Filter Parameters Reference
| Filter | Parameter | Range | Default | Description |
|--------|-----------|-------|---------|-------------|
| grayscale | intensity | 0-1 | 1 | Effect strength |
| sepia | intensity | 0-1 | 1 | Effect strength |
| blur | radius | 0-20 | 5 | Blur amount |
| brightness | value | -100 to 100 | 0 | Brightness change |
| contrast | value | -100 to 100 | 0 | Contrast change |
| saturate | value | -100 to 100 | 0 | Saturation change |
| invert | intensity | 0-1 | 1 | Inversion amount |
| noise | intensity | 0-100 | 30 | Grain amount |
| noise | monochrome | boolean | false | B&W grain |
| vignette | intensity | 0-1 | 0.5 | Edge darkness |
| vignette | radius | 0-1 | 0.5 | Vignette size |
| posterize | levels | 2-32 | 4 | Color levels |
| colorOverlay | color | hex/rgb | - | Overlay color |
| colorOverlay | intensity | 0-1 | 0.5 | Overlay strength |
| sharpen | intensity | 0-100 | 50 | Sharpness |
| vintage | intensity | 0-1 | 0.5 | Overall effect |
`,
'pinepaper://docs/interactive-animations': `# Interactive Animations Guide
Create dynamic, game-like behaviors using PinePaper's relation system. These patterns enable responsive animations that feel alive.
## The Follows Relation
The \`follows\` relation makes an item move toward a target with configurable smoothing and delay.
### Basic Following
\`\`\`
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 30, color: "#3b82f6"}
→ leader
pinepaper_create_item itemType: "circle" position: {x: 300, y: 300} properties: {radius: 20, color: "#ef4444"}
→ follower
pinepaper_add_relation
sourceId: "follower"
targetId: "leader"
relationType: "follows"
params: {
smoothing: 0.1, // Lower = snappier, higher = smoother
delay: 0 // Seconds of lag
}
\`\`\`
### Snake/Trail Effect
Create a chain of followers with increasing delay:
\`\`\`
// Leader
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 20, color: "#22c55e"}
→ head
// Segment 1
pinepaper_create_item itemType: "circle" position: {x: 380, y: 300} properties: {radius: 18, color: "#16a34a"}
→ seg1
pinepaper_add_relation sourceId: "seg1" targetId: "head" relationType: "follows"
params: {smoothing: 0.15, delay: 0.1}
// Segment 2
pinepaper_create_item itemType: "circle" position: {x: 360, y: 300} properties: {radius: 16, color: "#15803d"}
→ seg2
pinepaper_add_relation sourceId: "seg2" targetId: "seg1" relationType: "follows"
params: {smoothing: 0.15, delay: 0.1}
// Segment 3
pinepaper_create_item itemType: "circle" position: {x: 340, y: 300} properties: {radius: 14, color: "#166534"}
→ seg3
pinepaper_add_relation sourceId: "seg3" targetId: "seg2" relationType: "follows"
params: {smoothing: 0.15, delay: 0.1}
// Animate the head
pinepaper_add_relation sourceId: "head" targetId: "head" relationType: "orbits"
params: {radius: 100, speed: 0.5}
\`\`\`
### Elastic Following
Use low smoothing for a bouncy, elastic feel:
\`\`\`
pinepaper_add_relation
sourceId: "follower"
targetId: "leader"
relationType: "follows"
params: {smoothing: 0.02, delay: 0} // Very low smoothing = elastic
\`\`\`
## Maintaining Distance
The \`maintains_distance\` relation keeps an item at a fixed distance from a target.
### Orbiting Guard
\`\`\`
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 30, color: "#fbbf24"}
→ core
pinepaper_create_item itemType: "circle" position: {x: 500, y: 300} properties: {radius: 15, color: "#ef4444"}
→ guard
pinepaper_add_relation
sourceId: "guard"
targetId: "core"
relationType: "maintains_distance"
params: {
distance: 100, // Stay 100 pixels away
strength: 0.8 // How strongly to enforce (0-1)
}
\`\`\`
### Magnetic Repulsion Effect
Multiple items maintaining distance creates a repulsion field:
\`\`\`
// Central attractor
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 25, color: "#8b5cf6"}
→ center
// Floating particles that stay away
pinepaper_create_item itemType: "circle" position: {x: 300, y: 250} properties: {radius: 10, color: "#c4b5fd"}
→ p1
pinepaper_add_relation sourceId: "p1" targetId: "center" relationType: "maintains_distance"
params: {distance: 80, strength: 1}
pinepaper_create_item itemType: "circle" position: {x: 500, y: 350} properties: {radius: 10, color: "#c4b5fd"}
→ p2
pinepaper_add_relation sourceId: "p2" targetId: "center" relationType: "maintains_distance"
params: {distance: 80, strength: 1}
\`\`\`
## Bounded Movement
The \`bounds_to\` relation constrains an item within a boundary.
### Bouncing Ball
\`\`\`
// Create boundary rectangle (invisible or visible)
pinepaper_create_item itemType: "rectangle" position: {x: 400, y: 300}
properties: {width: 300, height: 200, fillColor: null, strokeColor: "#374151", strokeWidth: 2}
→ bounds
// Create bouncing ball
pinepaper_create_item itemType: "circle" position: {x: 350, y: 280} properties: {radius: 15, color: "#ef4444"}
→ ball
pinepaper_add_relation
sourceId: "ball"
targetId: "bounds"
relationType: "bounds_to"
params: {
padding: 15, // Stay this far from edges
bounce: true // Bounce off walls
}
// Give the ball some motion
pinepaper_animate_item itemId: "ball" animationType: "slide" speed: 2
\`\`\`
### Contained Particles
\`\`\`
// Container
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 120, fillColor: null, strokeColor: "#3b82f6", strokeWidth: 2}
→ container
// Particles inside
pinepaper_create_item itemType: "circle" position: {x: 380, y: 290} properties: {radius: 8, color: "#60a5fa"}
→ p1
pinepaper_add_relation sourceId: "p1" targetId: "container" relationType: "bounds_to"
params: {padding: 10, bounce: true}
pinepaper_animate_item itemId: "p1" animationType: "bounce" speed: 1.5
pinepaper_create_item itemType: "circle" position: {x: 420, y: 310} properties: {radius: 8, color: "#60a5fa"}
→ p2
pinepaper_add_relation sourceId: "p2" targetId: "container" relationType: "bounds_to"
params: {padding: 10, bounce: true}
pinepaper_animate_item itemId: "p2" animationType: "wobble" speed: 1.2
\`\`\`
## Points At (Rotation Tracking)
The \`points_at\` relation makes an item rotate to face a target.
### Compass Needle
\`\`\`
// North marker
pinepaper_create_item itemType: "circle" position: {x: 400, y: 200} properties: {radius: 10, color: "#ef4444"}
→ north
// Compass arrow
pinepaper_create_item itemType: "path" position: {x: 400, y: 300}
properties: {
segments: [[0, -40], [10, 20], [0, 10], [-10, 20]],
closed: true,
fillColor: "#1e40af"
}
→ needle
pinepaper_add_relation
sourceId: "needle"
targetId: "north"
relationType: "points_at"
params: {
offset_angle: 0, // Additional rotation offset
smoothing: 0.1 // Rotation smoothing
}
\`\`\`
### Tracking Eyes
\`\`\`
// Create eye
pinepaper_create_item itemType: "circle" position: {x: 380, y: 300} properties: {radius: 30, color: "#ffffff"}
→ eye_white
pinepaper_create_item itemType: "circle" position: {x: 380, y: 300} properties: {radius: 12, color: "#1f2937"}
→ pupil
// Target to look at (could be animated)
pinepaper_create_item itemType: "circle" position: {x: 500, y: 250} properties: {radius: 8, color: "#ef4444"}
→ target
// Pupil points at target
pinepaper_add_relation sourceId: "pupil" targetId: "target" relationType: "points_at"
params: {smoothing: 0.2}
// Animate target in circle
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 3, color: null}
→ orbit_center
pinepaper_add_relation sourceId: "target" targetId: "orbit_center" relationType: "orbits"
params: {radius: 150, speed: 0.3}
\`\`\`
## Mirroring
The \`mirrors\` relation creates symmetrical movement.
### Horizontal Mirror
\`\`\`
// Original
pinepaper_create_item itemType: "star" position: {x: 300, y: 300}
properties: {points: 5, radius1: 30, radius2: 15, color: "#fbbf24"}
→ original
// Mirror
pinepaper_create_item itemType: "star" position: {x: 500, y: 300}
properties: {points: 5, radius1: 30, radius2: 15, color: "#f59e0b"}
→ mirror
pinepaper_add_relation
sourceId: "mirror"
targetId: "original"
relationType: "mirrors"
params: {
axis: "vertical", // Mirror across vertical axis
center: [400, 300] // Mirror center point
}
// Animate original - mirror follows
pinepaper_animate_item itemId: "original" animationType: "bounce" speed: 1
\`\`\`
### Kaleidoscope Effect (4-way mirror)
\`\`\`
// Original in top-left quadrant
pinepaper_create_item itemType: "circle" position: {x: 350, y: 250} properties: {radius: 15, color: "#3b82f6"}
→ orig
// Horizontal mirror
pinepaper_create_item itemType: "circle" position: {x: 450, y: 250} properties: {radius: 15, color: "#8b5cf6"}
→ h_mirror
pinepaper_add_relation sourceId: "h_mirror" targetId: "orig" relationType: "mirrors"
params: {axis: "vertical", center: [400, 300]}
// Vertical mirror
pinepaper_create_item itemType: "circle" position: {x: 350, y: 350} properties: {radius: 15, color: "#ec4899"}
→ v_mirror
pinepaper_add_relation sourceId: "v_mirror" targetId: "orig" relationType: "mirrors"
params: {axis: "horizontal", center: [400, 300]}
// Both axes mirror
pinepaper_create_item itemType: "circle" position: {x: 450, y: 350} properties: {radius: 15, color: "#f43f5e"}
→ both_mirror
pinepaper_add_relation sourceId: "both_mirror" targetId: "orig" relationType: "mirrors"
params: {axis: "both", center: [400, 300]}
\`\`\`
## Parallax Depth
The \`parallax\` relation creates depth illusion through differential movement.
### Layered Background
\`\`\`
// Far background (moves least)
pinepaper_create_item itemType: "circle" position: {x: 200, y: 400} properties: {radius: 80, color: "#1e3a5f"}
→ far
pinepaper_add_relation sourceId: "far" targetId: "player" relationType: "parallax"
params: {depth: 0.2, origin: [400, 300]}
// Mid layer
pinepaper_create_item itemType: "circle" position: {x: 300, y: 350} properties: {radius: 50, color: "#3b82f6"}
→ mid
pinepaper_add_relation sourceId: "mid" targetId: "player" relationType: "parallax"
params: {depth: 0.5, origin: [400, 300]}
// Near foreground (moves most)
pinepaper_create_item itemType: "circle" position: {x: 450, y: 300} properties: {radius: 30, color: "#60a5fa"}
→ near
pinepaper_add_relation sourceId: "near" targetId: "player" relationType: "parallax"
params: {depth: 0.8, origin: [400, 300]}
// Player/camera reference
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 10, color: "#ffffff"}
→ player
\`\`\`
## Combining Relations
### Orbiting Follower Chain
\`\`\`
// Center
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 20, color: "#fbbf24"}
→ sun
// Orbiting planet
pinepaper_create_item itemType: "circle" position: {x: 500, y: 300} properties: {radius: 15, color: "#3b82f6"}
→ planet
pinepaper_add_relation sourceId: "planet" targetId: "sun" relationType: "orbits"
params: {radius: 100, speed: 0.5}
// Moon follows planet with delay
pinepaper_create_item itemType: "circle" position: {x: 520, y: 300} properties: {radius: 8, color: "#9ca3af"}
→ moon
pinepaper_add_relation sourceId: "moon" targetId: "planet" relationType: "follows"
params: {offset: [30, 0], smoothing: 0.1, delay: 0.2}
// Comet maintains distance but also follows
pinepaper_create_item itemType: "circle" position: {x: 300, y: 200} properties: {radius: 6, color: "#67e8f9"}
→ comet
pinepaper_add_relation sourceId: "comet" targetId: "planet" relationType: "maintains_distance"
params: {distance: 60, strength: 0.5}
\`\`\`
### Game Character
\`\`\`
// Character body
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300} properties: {radius: 25, color: "#3b82f6"}
→ body
// Eyes that point at mouse/target
pinepaper_create_item itemType: "circle" position: {x: 395, y: 290} properties: {radius: 5, color: "#ffffff"}
→ eye1
pinepaper_add_relation sourceId: "eye1" targetId: "body" relationType: "attached_to"
params: {offset: [-5, -10]}
pinepaper_create_item itemType: "circle" position: {x: 405, y: 290} properties: {radius: 5, color: "#ffffff"}
→ eye2
pinepaper_add_relation sourceId: "eye2" targetId: "body" relationType: "attached_to"
params: {offset: [5, -10]}
// Shadow follows with offset
pinepaper_create_item itemType: "ellipse" position: {x: 400, y: 330}
properties: {width: 40, height: 15, color: "#00000044"}
→ shadow
pinepaper_add_relation sourceId: "shadow" targetId: "body" relationType: "follows"
params: {offset: [0, 30], smoothing: 0.05}
\`\`\`
## Relation Parameters Quick Reference
| Relation | Key Params | Effect |
|----------|-----------|--------|
| follows | smoothing: 0.01-1 | Lower = snappier movement |
| follows | delay: 0-2 | Seconds of lag |
| maintains_distance | strength: 0-1 | Enforcement rigidity |
| bounds_to | bounce: true | Bounce off edges |
| bounds_to | padding: 0+ | Inner margin |
| points_at | smoothing: 0-1 | Rotation smoothness |
| points_at | offset_angle: -180 to 180 | Rotation offset |
| mirrors | axis: vertical/horizontal/both | Mirror axis |
| parallax | depth: 0-1 | Movement factor |
`,
'pinepaper://docs/paperjs-in-pinepaper': `# Paper.js in PinePaper
PinePaper is built on Paper.js - a powerful vector graphics library. You can write Paper.js code directly in PinePaper for maximum control and flexibility.
## Two Ways to Create Graphics
### 1. MCP Tools (Recommended for AI)
Natural language-friendly, declarative approach:
\`\`\`
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 50, color: "#3b82f6"}
\`\`\`
### 2. Direct Paper.js Code (Full Control)
Write JavaScript using the Paper.js API:
\`\`\`javascript
var circle = new Path.Circle({
center: new Point(400, 300),
radius: 50,
fillColor: '#3b82f6'
});
\`\`\`
Both approaches work in PinePaper - use whichever fits your needs!
## Paper.js Quick Reference
### The Global Objects
PinePaper provides these Paper.js globals:
- \`paper\` - The paper scope
- \`Path\` - Path constructors
- \`Point\` - Point constructor
- \`Size\` - Size constructor
- \`Color\` - Color constructor
- \`Group\` - Group constructor
- \`view\` - The canvas view
- \`project\` - The active project
### Creating Shapes
**Circle:**
\`\`\`javascript
var circle = new Path.Circle({
center: new Point(400, 300),
radius: 50,
fillColor: '#3b82f6',
strokeColor: '#1e40af',
strokeWidth: 2
});
\`\`\`
**Rectangle:**
\`\`\`javascript
var rect = new Path.Rectangle({
point: new Point(350, 250),
size: new Size(100, 100),
fillColor: '#22c55e',
radius: 10 // Rounded corners
});
\`\`\`
**Star:**
\`\`\`javascript
var star = new Path.Star({
center: new Point(400, 300),
points: 5,
radius1: 50, // Outer radius
radius2: 25, // Inner radius
fillColor: '#fbbf24'
});
\`\`\`
**Regular Polygon:**
\`\`\`javascript
var hexagon = new Path.RegularPolygon({
center: new Point(400, 300),
sides: 6,
radius: 50,
fillColor: '#8b5cf6'
});
\`\`\`
**Line:**
\`\`\`javascript
var line = new Path.Line({
from: new Point(100, 100),
to: new Point(300, 200),
strokeColor: '#ef4444',
strokeWidth: 3
});
\`\`\`
**Arc:**
\`\`\`javascript
var arc = new Path.Arc({
from: new Point(100, 100),
through: new Point(150, 50),
to: new Point(200, 100),
strokeColor: '#06b6d4',
strokeWidth: 2
});
\`\`\`
### Creating Paths
**From Points:**
\`\`\`javascript
var path = new Path({
segments: [
new Point(100, 100),
new Point(200, 50),
new Point(300, 100),
new Point(250, 200)
],
strokeColor: '#10b981',
strokeWidth: 2,
closed: true
});
path.smooth(); // Smooth the curves
\`\`\`
**Using SVG Path Data:**
\`\`\`javascript
var path = new Path(
'M 0 -50 C 50 -50 50 50 0 50 C -50 50 -50 -50 0 -50'
);
path.position = new Point(400, 300);
path.fillColor = '#ec4899';
\`\`\`
**Building Paths Segment by Segment:**
\`\`\`javascript
var path = new Path();
path.strokeColor = '#f59e0b';
path.strokeWidth = 3;
path.moveTo(new Point(100, 100));
path.lineTo(new Point(200, 100));
path.curveTo(new Point(250, 50), new Point(300, 100));
path.closePath();
\`\`\`
### Styling Properties
\`\`\`javascript
item.fillColor = '#3b82f6'; // Fill color
item.strokeColor = '#1e40af'; // Stroke color
item.strokeWidth = 2; // Stroke width
item.strokeCap = 'round'; // 'butt', 'round', 'square'
item.strokeJoin = 'round'; // 'miter', 'round', 'bevel'
item.dashArray = [10, 5]; // Dashed line
item.opacity = 0.8; // Transparency (0-1)
item.shadowColor = '#000000'; // Shadow color
item.shadowBlur = 10; // Shadow blur
item.shadowOffset = new Point(5, 5); // Shadow offset
\`\`\`
### Transformations
\`\`\`javascript
// Position
item.position = new Point(400, 300);
item.position.x += 50;
// Rotation
item.rotation = 45; // Degrees
item.rotate(15); // Rotate by amount
// Scale
item.scaling = 1.5; // Uniform scale
item.scaling = new Point(2, 1); // Non-uniform
item.scale(1.2); // Scale by amount
// Flip
item.scale(-1, 1); // Flip horizontal
item.scale(1, -1); // Flip vertical
\`\`\`
### Groups
\`\`\`javascript
var circle = new Path.Circle({
center: new Point(0, 0),
radius: 30,
fillColor: '#3b82f6'
});
var square = new Path.Rectangle({
point: new Point(-20, -20),
size: new Size(40, 40),
fillColor: '#ef4444'
});
var group = new Group([circle, square]);
group.position = new Point(400, 300);
group.rotate(45); // Rotates entire group
\`\`\`
### Animation with onFrame
Paper.js provides frame-by-frame animation:
\`\`\`javascript
var circle = new Path.Circle({
center: view.center,
radius: 50,
fillColor: '#3b82f6'
});
var angle = 0;
function onFrame(event) {
// event.count - frame count
// event.time - total time
// event.delta - time since last frame
angle += 2;
circle.position.x = view.center.x + Math.cos(angle * Math.PI / 180) * 100;
circle.position.y = view.center.y + Math.sin(angle * Math.PI / 180) * 100;
}
\`\`\`
### Event Handling
\`\`\`javascript
// Mouse events on items
circle.onMouseEnter = function(event) {
this.fillColor = '#ef4444';
};
circle.onMouseLeave = function(event) {
this.fillColor = '#3b82f6';
};
circle.onClick = function(event) {
this.scale(1.2);
};
// Global tool events
function onMouseMove(event) {
// event.point - mouse position
circle.position = event.point;
}
function onMouseDown(event) {
var path = new Path.Circle({
center: event.point,
radius: 20,
fillColor: '#22c55e'
});
}
\`\`\`
### Hit Testing
\`\`\`javascript
function onMouseDown(event) {
var hitResult = project.hitTest(event.point, {
segments: true,
stroke: true,
fill: true,
tolerance: 5
});
if (hitResult) {
hitResult.item.selected = true;
}
}
\`\`\`
### Working with Text
\`\`\`javascript
var text = new PointText({
point: new Point(400, 300),
content: 'Hello PinePaper!',
fillColor: '#ffffff',
fontFamily: 'Arial',
fontWeight: 'bold',
fontSize: 48,
justification: 'center'
});
\`\`\`
### Importing SVG
\`\`\`javascript
// From string
var svgString = '<svg>...</svg>';
project.importSVG(svgString, function(item) {
item.position = view.center;
item.scale(2);
});
// Imported items become Paper.js Groups
\`\`\`
### Exporting SVG
\`\`\`javascript
var svg = project.exportSVG({ asString: true });
// Returns SVG string
\`\`\`
## Combining Paper.js with PinePaper Features
### Register Paper.js Items with PinePaper
After creating items with Paper.js, register them to use PinePaper's relation system:
\`\`\`javascript
// Create with Paper.js
var planet = new Path.Circle({
center: new Point(400, 300),
radius: 20,
fillColor: '#3b82f6'
});
// Register with PinePaper (enables relations, training data export)
var itemId = app.registerItem(planet, 'circle', { source: 'paperjs' });
// Now you can use relations
app.addRelation(itemId, 'item_1', 'orbits', { radius: 100, speed: 0.5 });
\`\`\`
### Access PinePaper's App Object
\`\`\`javascript
// app is the PinePaper application instance
app.setBackgroundColor('#0f172a');
app.getItemById('item_1');
app.addRelation(sourceId, targetId, 'follows', { smoothing: 0.1 });
app.exportAnimatedSVG();
\`\`\`
## Common Patterns
### Animated Background
\`\`\`javascript
var circles = [];
for (var i = 0; i < 20; i++) {
var circle = new Path.Circle({
center: new Point(
Math.random() * view.size.width,
Math.random() * view.size.height
),
radius: Math.random() * 30 + 10,
fillColor: new Color(Math.random(), Math.random(), Math.random(), 0.5)
});
circles.push({
item: circle,
speed: Math.random() * 2 + 0.5
});
}
function onFrame(event) {
for (var i = 0; i < circles.length; i++) {
circles[i].item.position.y -= circles[i].speed;
if (circles[i].item.position.y < -50) {
circles[i].item.position.y = view.size.height + 50;
}
}
}
\`\`\`
### Interactive Drawing
\`\`\`javascript
var path;
function onMouseDown(event) {
path = new Path({
strokeColor: '#22c55e',
strokeWidth: 3
});
path.add(event.point);
}
function onMouseDrag(event) {
path.add(event.point);
}
function onMouseUp(event) {
path.smooth();
}
\`\`\`
### Morphing Shapes
\`\`\`javascript
var shape1 = new Path.Circle({
center: view.center,
radius: 50,
fillColor: '#3b82f6'
});
var shape2 = new Path.Star({
center: view.center,
points: 5,
radius1: 80,
radius2: 40,
fillColor: '#ef4444',
visible: false
});
var morphing = false;
var t = 0;
shape1.onClick = function() {
morphing = !morphing;
};
function onFrame(event) {
if (morphing) {
t = Math.min(t + 0.02, 1);
} else {
t = Math.max(t - 0.02, 0);
}
// Interpolate between shapes
for (var i = 0; i < shape1.segments.length; i++) {
var seg1 = shape1.segments[i];
var seg2 = shape2.segments[i % shape2.segments.length];
seg1.point = seg1.point.add(
seg2.point.subtract(seg1.point).multiply(t * 0.1)
);
}
}
\`\`\`
## Paper.js Resources
- **Official Docs**: http://paperjs.org/reference/
- **Tutorials**: http://paperjs.org/tutorials/
- **Examples**: http://paperjs.org/examples/
## When to Use Paper.js vs MCP Tools
| Use Paper.js When | Use MCP Tools When |
|-------------------|-------------------|
| Need frame-by-frame animation control | Want declarative animation (orbits, follows) |
| Complex procedural generation | Creating standard shapes |
| Interactive mouse/keyboard handling | AI-driven content creation |
| Custom path manipulation algorithms | Building compositions with relations |
| Porting existing Paper.js code | Generating training data |
| Need maximum performance | Want simpler, higher-level API |
`,
'pinepaper://examples/logo-animation': `# Logo Animation Example
Create a professional animated logo with entrance effects and continuous motion.
## What You'll Build
An animated company logo featuring:
- Geometric shapes assembling together
- Text reveal with typewriter effect
- Continuous subtle animations
- Professional polish with effects
## Step 1: Canvas Setup
\`\`\`
pinepaper_clear_canvas # Browser auto-connects in agent mode
pinepaper_set_background_color color: "#0f172a"
pinepaper_set_canvas_size width: 800 height: 600
\`\`\`
## Step 2: Create Logo Mark (Geometric Icon)
Build an abstract logo from shapes:
\`\`\`
// Main hexagon
pinepaper_create_item
itemType: "polygon"
position: {x: 300, y: 280}
properties:
sides: 6
radius: 70
color: "#3b82f6"
→ hex_main
// Inner triangle pointing up
pinepaper_create_item
itemType: "path"
position: {x: 300, y: 280}
properties:
segments: [[0, -35], [30, 20], [-30, 20]]
closed: true
fillColor: "#1e40af"
strokeWidth: 0
→ triangle_inner
// Accent circle
pinepaper_create_item
itemType: "circle"
position: {x: 300, y: 255}
properties:
radius: 12
color: "#60a5fa"
→ accent_dot
\`\`\`
## Step 3: Animate Logo Assembly
Use keyframe animation to assemble the logo:
\`\`\`
// Hexagon scales in from nothing
pinepaper_keyframe_animate
itemId: "hex_main"
keyframes:
- time: 0
properties: {scale: 0, opacity: 0}
easing: "easeOut"
- time: 0.5
properties: {scale: 1.1, opacity: 1}
easing: "easeInOut"
- time: 0.7
properties: {scale: 1, opacity: 1}
duration: 0.7
// Triangle fades in and slides up
pinepaper_keyframe_animate
itemId: "triangle_inner"
keyframes:
- time: 0
properties: {opacity: 0, y: 300}
- time: 0.3
properties: {opacity: 0, y: 300}
- time: 0.6
properties: {opacity: 1, y: 280}
easing: "easeOut"
duration: 0.6
// Accent dot pops in
pinepaper_keyframe_animate
itemId: "accent_dot"
keyframes:
- time: 0
properties: {scale: 0}
- time: 0.5
properties: {scale: 0}
- time: 0.7
properties: {scale: 1.3}
easing: "elastic"
- time: 0.9
properties: {scale: 1}
duration: 0.9
\`\`\`
## Step 4: Add Company Name
\`\`\`
// Company name with typewriter effect
pinepaper_create_item
itemType: "text"
position: {x: 480, y: 260}
properties:
content: "ACME"
fontSize: 56
fontFamily: "Arial Black, sans-serif"
fontWeight: "bold"
color: "#ffffff"
→ company_name
// Tagline
pinepaper_create_item
itemType: "text"
position: {x: 480, y: 310}
properties:
content: "Innovation Labs"
fontSize: 20
fontFamily: "Arial, sans-serif"
color: "#94a3b8"
→ tagline
// Typewriter effect on company name
pinepaper_animate_item
itemId: "company_name"
animationType: "typewriter"
speed: 3
\`\`\`
## Step 5: Add Continuous Motion
Subtle animations that loop forever:
\`\`\`
// Hexagon has gentle pulse
pinepaper_animate_item
itemId: "hex_main"
animationType: "pulse"
speed: 0.3
// Accent dot orbits slightly
pinepaper_add_relation
sourceId: "accent_dot"
targetId: "hex_main"
relationType: "orbits"
params: {radius: 3, speed: 0.5}
// Triangle has subtle rotation
pinepaper_animate_item
itemId: "triangle_inner"
animationType: "wobble"
speed: 0.2
\`\`\`
## Step 6: Add Sparkle Effect
\`\`\`
// Sparkle on accent dot
pinepaper_apply_effect
itemId: "accent_dot"
effectType: "sparkle"
params:
color: "#93c5fd"
speed: 0.8
size: 2
\`\`\`
## Step 7: Add Decorative Elements
\`\`\`
// Subtle line under text
pinepaper_create_item
itemType: "line"
properties:
from: [400, 340]
to: [560, 340]
strokeColor: "#3b82f6"
strokeWidth: 2
→ underline
// Animated line reveal
pinepaper_keyframe_animate
itemId: "underline"
keyframes:
- time: 0
properties: {opacity: 0, scaleX: 0}
- time: 0.8
properties: {opacity: 0, scaleX: 0}
- time: 1.2
properties: {opacity: 1, scaleX: 1}
easing: "easeOut"
duration: 1.2
\`\`\`
## Step 8: Apply Film Look (Optional)
\`\`\`
pinepaper_add_filter filterType: "vignette" params: {intensity: 0.3, radius: 0.8}
\`\`\`
## Step 9: Verify and Export
\`\`\`
pinepaper_browser_screenshot
pinepaper_export_svg animated: true
\`\`\`
## Result
A professional logo animation with:
- Shapes assembling from nothing
- Text typing in
- Continuous subtle motion
- Sparkle accents
- Cinematic vignette
## Variations
### Minimalist Version
Remove sparkle and reduce animation speeds by 50%.
### Energetic Version
Increase all speeds, add more orbiting elements, use blast effect.
### Corporate Version
Use grayscale filter, slower animations, no sparkle.
## Key Techniques
| Effect | Technique |
|--------|-----------|
| Assembly animation | Keyframes with scale 0→1 |
| Text reveal | typewriter animation type |
| Bounce overshoot | elastic easing |
| Subtle motion | Low speed pulse/wobble |
| Polish | sparkle effect, vignette filter |
`,
'pinepaper://examples/data-visualization': `# Data Visualization Example
Create animated charts and infographics that bring data to life.
## Animated Bar Chart
### Step 1: Setup
\`\`\`
pinepaper_clear_canvas # Browser auto-connects in agent mode
pinepaper_set_background_color color: "#1f2937"
\`\`\`
### Step 2: Create Axes
\`\`\`
// Y-axis
pinepaper_create_item itemType: "line"
properties: {from: [100, 450], to: [100, 100], strokeColor: "#6b7280", strokeWidth: 2}
// X-axis
pinepaper_create_item itemType: "line"
properties: {from: [100, 450], to: [700, 450], strokeColor: "#6b7280", strokeWidth: 2}
// Y-axis labels
pinepaper_create_item itemType: "text" position: {x: 70, y: 450}
properties: {content: "0", fontSize: 14, color: "#9ca3af"}
pinepaper_create_item itemType: "text" position: {x: 70, y: 350}
properties: {content: "25", fontSize: 14, color: "#9ca3af"}
pinepaper_create_item itemType: "text" position: {x: 70, y: 250}
properties: {content: "50", fontSize: 14, color: "#9ca3af"}
pinepaper_create_item itemType: "text" position: {x: 70, y: 150}
properties: {content: "75", fontSize: 14, color: "#9ca3af"}
\`\`\`
### Step 3: Create Animated Bars
Data: Q1=45, Q2=62, Q3=38, Q4=71
\`\`\`
// Q1 Bar (height: 45% of 300px = 135px)
pinepaper_create_item itemType: "rectangle" position: {x: 200, y: 382}
properties: {width: 60, height: 135, color: "#3b82f6"}
→ bar_q1
// Q2 Bar (height: 62% = 186px)
pinepaper_create_item itemType: "rectangle" position: {x: 320, y: 357}
properties: {width: 60, height: 186, color: "#8b5cf6"}
→ bar_q2
// Q3 Bar (height: 38% = 114px)
pinepaper_create_item itemType: "rectangle" position: {x: 440, y: 393}
properties: {width: 60, height: 114, color: "#ec4899"}
→ bar_q3
// Q4 Bar (height: 71% = 213px)
pinepaper_create_item itemType: "rectangle" position: {x: 560, y: 343}
properties: {width: 60, height: 213, color: "#f59e0b"}
→ bar_q4
// X-axis labels
pinepaper_create_item itemType: "text" position: {x: 200, y: 480}
properties: {content: "Q1", fontSize: 16, color: "#ffffff"}
pinepaper_create_item itemType: "text" position: {x: 320, y: 480}
properties: {content: "Q2", fontSize: 16, color: "#ffffff"}
pinepaper_create_item itemType: "text" position: {x: 440, y: 480}
properties: {content: "Q3", fontSize: 16, color: "#ffffff"}
pinepaper_create_item itemType: "text" position: {x: 560, y: 480}
properties: {content: "Q4", fontSize: 16, color: "#ffffff"}
\`\`\`
### Step 4: Animate Bars Growing
\`\`\`
// Bars grow from bottom
pinepaper_keyframe_animate itemId: "bar_q1"
keyframes:
- time: 0
properties: {scaleY: 0}
- time: 0.5
properties: {scaleY: 1}
easing: "easeOut"
duration: 0.5
pinepaper_keyframe_animate itemId: "bar_q2"
keyframes:
- time: 0
properties: {scaleY: 0}
- time: 0.2
properties: {scaleY: 0}
- time: 0.7
properties: {scaleY: 1}
easing: "easeOut"
duration: 0.7
pinepaper_keyframe_animate itemId: "bar_q3"
keyframes:
- time: 0
properties: {scaleY: 0}
- time: 0.4
properties: {scaleY: 0}
- time: 0.9
properties: {scaleY: 1}
easing: "easeOut"
duration: 0.9
pinepaper_keyframe_animate itemId: "bar_q4"
keyframes:
- time: 0
properties: {scaleY: 0}
- time: 0.6
properties: {scaleY: 0}
- time: 1.1
properties: {scaleY: 1}
easing: "easeOut"
duration: 1.1
\`\`\`
---
## Animated Pie Chart
### Step 1: Create Pie Slices
Using arc paths for pie segments:
\`\`\`
pinepaper_set_background_color color: "#1e293b"
// Center point for reference
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 5, color: null}
→ center
// Slice 1: 40% (144 degrees) - Blue
pinepaper_create_item itemType: "path" position: {x: 400, y: 300}
properties:
pathData: "M 0 0 L 0 -100 A 100 100 0 0 1 95 31 Z"
fillColor: "#3b82f6"
strokeColor: "#1e293b"
strokeWidth: 2
→ slice1
// Slice 2: 25% (90 degrees) - Purple
pinepaper_create_item itemType: "path" position: {x: 400, y: 300}
properties:
pathData: "M 0 0 L 95 31 A 100 100 0 0 1 0 100 Z"
fillColor: "#8b5cf6"
strokeColor: "#1e293b"
strokeWidth: 2
→ slice2
// Slice 3: 20% (72 degrees) - Pink
pinepaper_create_item itemType: "path" position: {x: 400, y: 300}
properties:
pathData: "M 0 0 L 0 100 A 100 100 0 0 1 -95 31 Z"
fillColor: "#ec4899"
strokeColor: "#1e293b"
strokeWidth: 2
→ slice3
// Slice 4: 15% (54 degrees) - Orange
pinepaper_create_item itemType: "path" position: {x: 400, y: 300}
properties:
pathData: "M 0 0 L -95 31 A 100 100 0 0 1 0 -100 Z"
fillColor: "#f59e0b"
strokeColor: "#1e293b"
strokeWidth: 2
→ slice4
\`\`\`
### Step 2: Animate Pie Spinning In
\`\`\`
// Entire pie rotates in
pinepaper_animate_item itemId: "slice1" animationType: "rotate" speed: 0.1
pinepaper_keyframe_animate itemId: "slice1"
keyframes:
- time: 0
properties: {scale: 0, rotation: -180}
- time: 1
properties: {scale: 1, rotation: 0}
easing: "easeOut"
duration: 1
\`\`\`
### Step 3: Add Labels
\`\`\`
pinepaper_create_item itemType: "text" position: {x: 440, y: 240}
properties: {content: "40%", fontSize: 18, fontWeight: "bold", color: "#ffffff"}
→ label1
pinepaper_add_relation sourceId: "label1" targetId: "slice1" relationType: "attached_to"
params: {offset: [40, -60]}
\`\`\`
---
## Animated Line Graph
### Create Data Points and Line
\`\`\`
// Data points: (100,350), (200,280), (300,320), (400,200), (500,180), (600,250)
// Line connecting points
pinepaper_create_item itemType: "path" position: {x: 350, y: 265}
properties:
segments: [[-250, 85], [-150, 15], [-50, 55], [50, -65], [150, -85], [250, -15]]
smooth: true
closed: false
strokeColor: "#22c55e"
strokeWidth: 3
fillColor: null
→ data_line
// Data points as circles
pinepaper_create_item itemType: "circle" position: {x: 100, y: 350}
properties: {radius: 6, color: "#22c55e"}
→ point1
pinepaper_create_item itemType: "circle" position: {x: 200, y: 280}
properties: {radius: 6, color: "#22c55e"}
→ point2
pinepaper_create_item itemType: "circle" position: {x: 300, y: 320}
properties: {radius: 6, color: "#22c55e"}
→ point3
pinepaper_create_item itemType: "circle" position: {x: 400, y: 200}
properties: {radius: 6, color: "#22c55e"}
→ point4
pinepaper_create_item itemType: "circle" position: {x: 500, y: 180}
properties: {radius: 6, color: "#22c55e"}
→ point5
pinepaper_create_item itemType: "circle" position: {x: 600, y: 250}
properties: {radius: 6, color: "#22c55e"}
→ point6
\`\`\`
### Animate Points Appearing
\`\`\`
// Points pop in sequentially
pinepaper_keyframe_animate itemId: "point1"
keyframes:
- time: 0
properties: {scale: 0}
- time: 0.2
properties: {scale: 1.3}
easing: "easeOut"
- time: 0.3
properties: {scale: 1}
duration: 0.3
pinepaper_keyframe_animate itemId: "point2"
keyframes:
- time: 0
properties: {scale: 0}
- time: 0.15
properties: {scale: 0}
- time: 0.35
properties: {scale: 1.3}
- time: 0.45
properties: {scale: 1}
duration: 0.45
// ... continue for remaining points with staggered delays
\`\`\`
---
## Gauge/Meter Chart
\`\`\`
// Gauge background arc
pinepaper_create_item itemType: "path" position: {x: 400, y: 350}
properties:
pathData: "M -120 0 A 120 120 0 0 1 120 0"
strokeColor: "#374151"
strokeWidth: 20
fillColor: null
→ gauge_bg
// Gauge value arc (75% = 135 degrees)
pinepaper_create_item itemType: "path" position: {x: 400, y: 350}
properties:
pathData: "M -120 0 A 120 120 0 0 1 85 -85"
strokeColor: "#22c55e"
strokeWidth: 20
fillColor: null
→ gauge_value
// Center label
pinepaper_create_item itemType: "text" position: {x: 400, y: 350}
properties: {content: "75%", fontSize: 48, fontWeight: "bold", color: "#ffffff"}
→ gauge_label
// Needle
pinepaper_create_item itemType: "path" position: {x: 400, y: 350}
properties:
segments: [[0, 10], [-5, 0], [0, -90], [5, 0]]
closed: true
fillColor: "#ef4444"
→ needle
// Animate needle sweeping to value
pinepaper_keyframe_animate itemId: "needle"
keyframes:
- time: 0
properties: {rotation: -90}
- time: 1.5
properties: {rotation: 45}
easing: "easeOut"
duration: 1.5
\`\`\`
## Key Techniques for Data Viz
| Visualization | Key Techniques |
|---------------|----------------|
| Bar Chart | Rectangles + scaleY keyframes |
| Pie Chart | Arc paths + rotation |
| Line Graph | Smooth path + point circles |
| Gauge | Arc paths + needle rotation |
| Animation | Staggered keyframes, easeOut |
| Labels | Text with attached_to relations |
`,
'pinepaper://examples/particle-systems': `# Particle Systems Example
Build dynamic particle effects like fireworks, starfields, snow, and fire.
## Starfield / Space Background
### Step 1: Create Background
\`\`\`
pinepaper_clear_canvas # Browser auto-connects in agent mode
pinepaper_set_background_color color: "#000011"
\`\`\`
### Step 2: Create Star Layers (Parallax Depth)
\`\`\`
// Far stars (small, slow)
pinepaper_create_item itemType: "circle" position: {x: 100, y: 150} properties: {radius: 1, color: "#6b7280"}
pinepaper_create_item itemType: "circle" position: {x: 250, y: 80} properties: {radius: 1, color: "#6b7280"}
pinepaper_create_item itemType: "circle" position: {x: 450, y: 200} properties: {radius: 1, color: "#6b7280"}
pinepaper_create_item itemType: "circle" position: {x: 600, y: 120} properties: {radius: 1, color: "#6b7280"}
pinepaper_create_item itemType: "circle" position: {x: 700, y: 250} properties: {radius: 1, color: "#6b7280"}
// Mid stars (medium)
pinepaper_create_item itemType: "circle" position: {x: 150, y: 300} properties: {radius: 2, color: "#9ca3af"}
→ mid1
pinepaper_create_item itemType: "circle" position: {x: 350, y: 400} properties: {radius: 2, color: "#9ca3af"}
→ mid2
pinepaper_create_item itemType: "circle" position: {x: 550, y: 350} properties: {radius: 2, color: "#9ca3af"}
→ mid3
// Near stars (larger, twinkling)
pinepaper_create_item itemType: "circle" position: {x: 200, y: 250} properties: {radius: 3, color: "#e5e7eb"}
→ near1
pinepaper_create_item itemType: "circle" position: {x: 500, y: 180} properties: {radius: 3, color: "#e5e7eb"}
→ near2
pinepaper_create_item itemType: "circle" position: {x: 650, y: 400} properties: {radius: 3, color: "#e5e7eb"}
→ near3
\`\`\`
### Step 3: Add Twinkle Animation
\`\`\`
pinepaper_animate_item itemId: "near1" animationType: "fade" speed: 2
pinepaper_animate_item itemId: "near2" animationType: "fade" speed: 1.5
pinepaper_animate_item itemId: "near3" animationType: "fade" speed: 2.5
pinepaper_animate_item itemId: "mid1" animationType: "pulse" speed: 0.5
pinepaper_animate_item itemId: "mid2" animationType: "pulse" speed: 0.7
\`\`\`
### Step 4: Add Shooting Star
\`\`\`
// Shooting star head
pinepaper_create_item itemType: "circle" position: {x: 100, y: 100} properties: {radius: 4, color: "#fef3c7"}
→ shooting_star
// Shooting star tail
pinepaper_create_item itemType: "path" position: {x: 100, y: 100}
properties:
segments: [[0, 0], [-30, 15], [-80, 40], [-150, 75]]
smooth: true
strokeColor: "#fef3c7"
strokeWidth: 2
opacity: 0.5
fillColor: null
→ star_tail
// Tail follows star
pinepaper_add_relation sourceId: "star_tail" targetId: "shooting_star" relationType: "attached_to"
params: {offset: [0, 0]}
// Animate shooting star across screen
pinepaper_keyframe_animate itemId: "shooting_star"
keyframes:
- time: 0
properties: {x: 100, y: 100, opacity: 1}
- time: 2
properties: {x: 700, y: 400, opacity: 0}
duration: 2
loop: true
\`\`\`
---
## Snowfall Effect
\`\`\`
pinepaper_set_background_color color: "#1e3a5f"
// Create snowflakes at different sizes and speeds
// Large, slow flakes (foreground)
pinepaper_create_item itemType: "circle" position: {x: 150, y: 50} properties: {radius: 5, color: "#ffffff"}
→ snow1
pinepaper_animate_item itemId: "snow1" animationType: "bounce" speed: 0.3
pinepaper_animate_item itemId: "snow1" animationType: "wobble" speed: 0.5
pinepaper_create_item itemType: "circle" position: {x: 400, y: 100} properties: {radius: 4, color: "#ffffff"}
→ snow2
pinepaper_animate_item itemId: "snow2" animationType: "bounce" speed: 0.35
pinepaper_animate_item itemId: "snow2" animationType: "wobble" speed: 0.6
pinepaper_create_item itemType: "circle" position: {x: 650, y: 30} properties: {radius: 5, color: "#ffffff"}
→ snow3
pinepaper_animate_item itemId: "snow3" animationType: "bounce" speed: 0.25
pinepaper_animate_item itemId: "snow3" animationType: "wobble" speed: 0.4
// Medium flakes
pinepaper_create_item itemType: "circle" position: {x: 250, y: 150} properties: {radius: 3, color: "#e5e7eb"}
→ snow4
pinepaper_animate_item itemId: "snow4" animationType: "bounce" speed: 0.4
pinepaper_create_item itemType: "circle" position: {x: 500, y: 80} properties: {radius: 3, color: "#e5e7eb"}
→ snow5
pinepaper_animate_item itemId: "snow5" animationType: "bounce" speed: 0.45
// Small, fast flakes (background)
pinepaper_create_item itemType: "circle" position: {x: 100, y: 200} properties: {radius: 2, color: "#d1d5db"}
→ snow6
pinepaper_animate_item itemId: "snow6" animationType: "bounce" speed: 0.6
pinepaper_create_item itemType: "circle" position: {x: 350, y: 250} properties: {radius: 2, color: "#d1d5db"}
→ snow7
pinepaper_animate_item itemId: "snow7" animationType: "bounce" speed: 0.55
\`\`\`
---
## Fireworks Display
\`\`\`
pinepaper_set_background_color color: "#0a0a1a"
// Firework 1: Red burst
pinepaper_create_item itemType: "circle" position: {x: 250, y: 200} properties: {radius: 8, color: "#ef4444"}
→ fw1_center
pinepaper_apply_effect itemId: "fw1_center" effectType: "blast"
params: {color: "#fca5a5", radius: 120, count: 25}
// Animate burst
pinepaper_keyframe_animate itemId: "fw1_center"
keyframes:
- time: 0
properties: {scale: 0, y: 500}
- time: 0.5
properties: {scale: 0.5, y: 200}
- time: 0.7
properties: {scale: 1, y: 200}
- time: 1.5
properties: {scale: 0, opacity: 0}
duration: 1.5
loop: true
// Firework 2: Gold burst
pinepaper_create_item itemType: "circle" position: {x: 500, y: 180} properties: {radius: 8, color: "#fbbf24"}
→ fw2_center
pinepaper_apply_effect itemId: "fw2_center" effectType: "blast"
params: {color: "#fef3c7", radius: 100, count: 20}
// Sparkle on bursts
pinepaper_apply_effect itemId: "fw1_center" effectType: "sparkle"
params: {color: "#fca5a5", speed: 2, size: 3}
pinepaper_apply_effect itemId: "fw2_center" effectType: "sparkle"
params: {color: "#fef3c7", speed: 2, size: 3}
\`\`\`
---
## Fire/Flame Effect
\`\`\`
pinepaper_set_background_color color: "#1a1a2e"
// Fire base (log)
pinepaper_create_item itemType: "rectangle" position: {x: 400, y: 480}
properties: {width: 120, height: 30, color: "#78350f", cornerRadius: 5}
// Flame layers (outer to inner)
// Outer red glow
pinepaper_create_item itemType: "path" position: {x: 400, y: 400}
properties:
pathData: "M 0 60 Q -50 30 -40 -20 Q -20 -60 0 -80 Q 20 -60 40 -20 Q 50 30 0 60"
fillColor: "#dc2626"
opacity: 0.6
→ flame_outer
// Middle orange
pinepaper_create_item itemType: "path" position: {x: 400, y: 410}
properties:
pathData: "M 0 45 Q -35 20 -28 -15 Q -14 -45 0 -60 Q 14 -45 28 -15 Q 35 20 0 45"
fillColor: "#f59e0b"
opacity: 0.8
→ flame_mid
// Inner yellow
pinepaper_create_item itemType: "path" position: {x: 400, y: 420}
properties:
pathData: "M 0 30 Q -20 10 -15 -10 Q -8 -30 0 -40 Q 8 -30 15 -10 Q 20 10 0 30"
fillColor: "#fbbf24"
→ flame_inner
// Core white
pinepaper_create_item itemType: "path" position: {x: 400, y: 430}
properties:
pathData: "M 0 15 Q -8 5 -5 -5 Q -3 -15 0 -20 Q 3 -15 5 -5 Q 8 5 0 15"
fillColor: "#fef3c7"
→ flame_core
// Animate flames
pinepaper_animate_item itemId: "flame_outer" animationType: "wobble" speed: 3
pinepaper_animate_item itemId: "flame_mid" animationType: "wobble" speed: 3.5
pinepaper_animate_item itemId: "flame_inner" animationType: "wobble" speed: 4
pinepaper_animate_item itemId: "flame_core" animationType: "wobble" speed: 4.5
// Add flickering
pinepaper_animate_item itemId: "flame_outer" animationType: "fade" speed: 5
pinepaper_animate_item itemId: "flame_mid" animationType: "fade" speed: 6
// Sparks rising
pinepaper_create_item itemType: "circle" position: {x: 390, y: 380} properties: {radius: 2, color: "#fbbf24"}
→ spark1
pinepaper_animate_item itemId: "spark1" animationType: "bounce" speed: 2
pinepaper_animate_item itemId: "spark1" animationType: "fade" speed: 3
pinepaper_create_item itemType: "circle" position: {x: 410, y: 370} properties: {radius: 2, color: "#f59e0b"}
→ spark2
pinepaper_animate_item itemId: "spark2" animationType: "bounce" speed: 2.2
pinepaper_animate_item itemId: "spark2" animationType: "fade" speed: 2.5
\`\`\`
---
## Bubble Float Effect
\`\`\`
pinepaper_set_background_color color: "#0ea5e9"
// Create bubbles with different sizes
pinepaper_create_item itemType: "circle" position: {x: 150, y: 500}
properties: {radius: 20, fillColor: null, strokeColor: "#ffffff", strokeWidth: 2, opacity: 0.6}
→ bubble1
pinepaper_create_item itemType: "circle" position: {x: 300, y: 520}
properties: {radius: 15, fillColor: null, strokeColor: "#ffffff", strokeWidth: 2, opacity: 0.5}
→ bubble2
pinepaper_create_item itemType: "circle" position: {x: 450, y: 480}
properties: {radius: 25, fillColor: null, strokeColor: "#ffffff", strokeWidth: 2, opacity: 0.7}
→ bubble3
pinepaper_create_item itemType: "circle" position: {x: 600, y: 510}
properties: {radius: 12, fillColor: null, strokeColor: "#ffffff", strokeWidth: 2, opacity: 0.5}
→ bubble4
// Add highlight to each bubble
pinepaper_create_item itemType: "circle" position: {x: 145, y: 490}
properties: {radius: 4, color: "#ffffff", opacity: 0.8}
→ highlight1
pinepaper_add_relation sourceId: "highlight1" targetId: "bubble1" relationType: "attached_to"
params: {offset: [-5, -10]}
// Animate bubbles floating up with wobble
pinepaper_animate_item itemId: "bubble1" animationType: "bounce" speed: 0.3
pinepaper_animate_item itemId: "bubble1" animationType: "wobble" speed: 0.5
pinepaper_animate_item itemId: "bubble2" animationType: "bounce" speed: 0.35
pinepaper_animate_item itemId: "bubble2" animationType: "wobble" speed: 0.6
pinepaper_animate_item itemId: "bubble3" animationType: "bounce" speed: 0.25
pinepaper_animate_item itemId: "bubble3" animationType: "wobble" speed: 0.4
pinepaper_animate_item itemId: "bubble4" animationType: "bounce" speed: 0.4
pinepaper_animate_item itemId: "bubble4" animationType: "wobble" speed: 0.7
\`\`\`
## Particle System Techniques
| Effect | Key Techniques |
|--------|----------------|
| Stars | Multiple sizes, fade animation, parallax layers |
| Snow | bounce + wobble, size variation, opacity variation |
| Fireworks | blast effect + sparkle, keyframe rise animation |
| Fire | Layered paths, wobble + fade, warm color gradient |
| Bubbles | Stroke-only circles, attached highlights, bounce |
| Rain | Lines at angle, fast bounce, blue tints |
| Dust | Tiny circles, slow fade, random positions |
| Confetti | Small rectangles, rotate + bounce, rainbow colors |
`,
'pinepaper://docs/map-tools': `# Map Tools Guide
Create geographic visualizations with world maps, choropleth coloring, markers, and labels.
## Available Maps
| Map | Description | Region ID Format |
|-----|-------------|------------------|
| \`world\` | World countries (110m) | Full names: "United States of America", "France" |
| \`worldHighRes\` | World countries (50m, detailed) | Full names |
| \`usa\` | US states | State codes: "CA", "TX", "NY" |
## Projections
| Projection | Best For |
|------------|----------|
| \`naturalEarth\` | World maps (recommended) |
| \`mercator\` | Web-style maps |
| \`equalEarth\` | Equal-area comparisons |
| \`orthographic\` | Globe/3D sphere view |
| \`albers\` | USA maps (recommended) |
| \`stereographic\` | Polar regions |
## Loading Maps
\`\`\`
// Load world map with Natural Earth projection
pinepaper_load_map mapId: "world"
options:
projection: "naturalEarth"
quality: "balanced"
fillColor: "#e5e7eb"
strokeColor: "#9ca3af"
enableHover: true
// Load USA with Albers projection
pinepaper_load_map mapId: "usa"
options:
projection: "albers"
quality: "professional"
\`\`\`
## Highlighting Regions
\`\`\`
// Highlight countries (use full names for world map)
pinepaper_highlight_regions
regionIds: ["United States of America", "Canada", "Mexico"]
options:
fillColor: "#3b82f6"
strokeColor: "#1d4ed8"
animate: true
// Highlight US states (use state codes)
pinepaper_highlight_regions
regionIds: ["CA", "TX", "NY", "FL"]
options:
fillColor: "#22c55e"
// Remove highlights
pinepaper_unhighlight_regions regionIds: "all"
\`\`\`
## Choropleth (Data-Driven Colors)
\`\`\`
// Population data visualization
pinepaper_apply_data_colors
data:
"California": 39538223
"Texas": 29145505
"Florida": 21538187
"New York": 20201249
options:
colorScale: "blues"
showLegend: true
legendPosition: "bottom-right"
legendTitle: "Population"
\`\`\`
### Color Scales
- \`blues\` - Light to dark blue
- \`greens\` - Light to dark green
- \`reds\` - Light to dark red
- \`oranges\` - Light to dark orange
- \`purples\` - Light to dark purple
- \`heat\` - Yellow to red heat map
## Map Markers
\`\`\`
// Add marker at coordinates
pinepaper_add_marker
lat: 37.7749
lon: -122.4194
label: "San Francisco"
color: "#ef4444"
size: 8
pulse: true
// Add marker with different shape
pinepaper_add_marker
lat: 40.7128
lon: -74.0060
label: "New York"
shape: "pin"
\`\`\`
### Marker Shapes
- \`circle\` - Simple circle (default)
- \`pin\` - Map pin icon
- \`star\` - Star shape
## Map Labels
\`\`\`
// Add labels to all visible regions
pinepaper_add_map_labels
options:
fontSize: 10
fontColor: "#374151"
labelType: "name"
// Add labels to specific regions
pinepaper_add_map_labels
regions: ["California", "Texas"]
options:
labelType: "code"
\`\`\`
### Label Types
- \`name\` - Full region name
- \`code\` - Region code (e.g., "CA")
- \`value\` - Data value (after applyDataColors)
## Map Navigation
\`\`\`
// Pan to coordinates
pinepaper_pan_map lat: 50 lon: 10 animate: true duration: 1
// Zoom in
pinepaper_zoom_map level: 2 animate: true
\`\`\`
## Importing Custom Maps
\`\`\`
// Import from URL
pinepaper_import_custom_map
url: "https://example.com/custom-boundaries.geojson"
options:
projection: "mercator"
// Import GeoJSON directly
pinepaper_import_custom_map
geoJson: { "type": "FeatureCollection", "features": [...] }
options:
projection: "naturalEarth"
\`\`\`
## Example: US Population Map
\`\`\`
// 1. Load USA map
pinepaper_load_map mapId: "usa"
options:
projection: "albers"
quality: "professional"
fillColor: "#e5e7eb"
strokeColor: "#9ca3af"
strokeWidth: 0.5
enableHover: true
// 2. Apply population data
pinepaper_apply_data_colors
data:
"CA": 39538223
"TX": 29145505
"FL": 21538187
"NY": 20201249
"PA": 13002700
"IL": 12812508
"OH": 11799448
"GA": 10711908
options:
colorScale: "blues"
showLegend: true
legendTitle: "Population"
// 3. Add major city markers
pinepaper_add_marker lat: 34.0522 lon: -118.2437 label: "Los Angeles" pulse: true
pinepaper_add_marker lat: 40.7128 lon: -74.0060 label: "New York" pulse: true
pinepaper_add_marker lat: 41.8781 lon: -87.6298 label: "Chicago" pulse: true
// 4. Export final map
pinepaper_export_map
\`\`\`
## Example: World Comparison
\`\`\`
// 1. Load world map
pinepaper_load_map mapId: "worldHighRes"
options:
projection: "naturalEarth"
quality: "balanced"
fillColor: "#f3f4f6"
strokeColor: "#d1d5db"
// 2. Highlight regions of interest
pinepaper_highlight_regions
regionIds: ["United States of America", "China", "Japan", "Germany", "United Kingdom"]
options:
fillColor: "#3b82f6"
animate: true
// 3. Add capital markers
pinepaper_add_marker lat: 38.9072 lon: -77.0369 label: "Washington DC"
pinepaper_add_marker lat: 39.9042 lon: 116.4074 label: "Beijing"
pinepaper_add_marker lat: 35.6762 lon: 139.6503 label: "Tokyo"
pinepaper_add_marker lat: 52.5200 lon: 13.4050 label: "Berlin"
pinepaper_add_marker lat: 51.5074 lon: -0.1278 label: "London"
\`\`\`
## Hit Testing
\`\`\`
// Find region at canvas coordinates
pinepaper_get_region_at_point x: 400 y: 300
// Returns: { regionId, regionName, properties }
\`\`\`
## Region Animations
Animate map regions with keyframe-based color transitions:
\`\`\`
// Animate specific regions with keyframes
pinepaper_animate_map_regions
duration: 8
loop: true
regions:
"USA":
- time: 0, fillColor: "#ef4444"
- time: 4, fillColor: "#22c55e"
- time: 8, fillColor: "#ef4444"
"France":
- time: 0, fillColor: "#3b82f6"
- time: 8, fillColor: "#fbbf24"
// Wave animation across all regions
pinepaper_animate_map_wave
duration: 10
loop: true
colors: ["#ef4444", "#fbbf24", "#22c55e", "#3b82f6"]
waveDirection: "horizontal"
// Stop animations
pinepaper_stop_map_animations
pinepaper_stop_map_animations regions: ["USA", "France"] resetColors: true
// Query animated regions
pinepaper_get_animated_map_regions
\`\`\`
## CSV Export & Import
Export and import map state via CSV for data-driven workflows:
\`\`\`
// Export region data as CSV
pinepaper_export_map_region_csv
includeHighlighted: true
includeColors: true
download: true
filename: "map-export.csv"
// Import CSV to update regions
pinepaper_import_map_region_csv
csvText: "regionId,fillColor,highlighted\\nUSA,#22c55e,1\\nFrance,#3b82f6,0"
applyColors: true
applyHighlight: true
\`\`\`
## Programmatic Selection
Control region selection programmatically:
\`\`\`
// Select regions
pinepaper_select_map_regions regionIds: ["USA", "Canada", "Mexico"]
// Deselect specific regions
pinepaper_deselect_map_regions regionIds: ["USA"]
// Deselect all
pinepaper_deselect_map_regions
// Query highlighted regions
pinepaper_get_highlighted_map_regions
\`\`\`
`,
'pinepaper://examples/world-population-map': `# World Population Map Example
Create an animated choropleth map showing world population data with country highlights and major city markers.
## Final Result
A beautiful world map showing population density by color intensity, with pulsing markers on major capitals.
---
## Step 1: Set Up Canvas and Background
\`\`\`
// Set dark background for contrast
pinepaper_set_background_color color: "#1a1a2e"
// Set canvas size for presentation
pinepaper_set_canvas_size preset: "presentation-16x9"
\`\`\`
---
## Step 2: Load World Map
\`\`\`
pinepaper_load_map mapId: "worldHighRes"
options:
projection: "naturalEarth"
quality: "professional"
fillColor: "#374151"
strokeColor: "#4b5563"
strokeWidth: 0.3
enableHover: true
hoverFill: "#6b7280"
\`\`\`
---
## Step 3: Apply Population Data (Choropleth)
\`\`\`
pinepaper_apply_data_colors
data:
"China": 1412
"India": 1408
"United States of America": 332
"Indonesia": 276
"Pakistan": 229
"Brazil": 215
"Nigeria": 218
"Bangladesh": 169
"Russia": 144
"Mexico": 130
"Japan": 125
"Ethiopia": 120
"Philippines": 113
"Egypt": 104
"Vietnam": 99
"Germany": 84
"Turkey": 85
"Iran": 87
"Thailand": 70
"United Kingdom": 67
"France": 68
"Italy": 59
"South Africa": 60
"Tanzania": 63
"Kenya": 54
"South Korea": 52
"Colombia": 51
"Spain": 47
"Argentina": 46
"Algeria": 45
"Sudan": 45
"Ukraine": 41
"Iraq": 42
"Poland": 38
"Canada": 38
"Morocco": 37
"Saudi Arabia": 35
"Peru": 33
"Malaysia": 33
"Australia": 26
options:
colorScale: "blues"
showLegend: true
legendPosition: "bottom-right"
legendTitle: "Population (millions)"
\`\`\`
---
## Step 4: Highlight Top 5 Countries
\`\`\`
pinepaper_highlight_regions
regionIds: ["China", "India", "United States of America", "Indonesia", "Pakistan"]
options:
strokeColor: "#fbbf24"
strokeWidth: 2
animate: true
\`\`\`
---
## Step 5: Add Capital City Markers
\`\`\`
// Beijing - China
pinepaper_add_marker lat: 39.9042 lon: 116.4074
label: "Beijing"
color: "#ef4444"
size: 10
pulse: true
// New Delhi - India
pinepaper_add_marker lat: 28.6139 lon: 77.2090
label: "New Delhi"
color: "#ef4444"
size: 10
pulse: true
// Washington DC - USA
pinepaper_add_marker lat: 38.9072 lon: -77.0369
label: "Washington DC"
color: "#ef4444"
size: 8
pulse: true
// Jakarta - Indonesia
pinepaper_add_marker lat: -6.2088 lon: 106.8456
label: "Jakarta"
color: "#f59e0b"
size: 8
pulse: true
// Islamabad - Pakistan
pinepaper_add_marker lat: 33.6844 lon: 73.0479
label: "Islamabad"
color: "#f59e0b"
size: 8
pulse: true
\`\`\`
---
## Step 6: Add Title
\`\`\`
pinepaper_create_item itemType: "text"
position: {x: 640, y: 50}
properties:
content: "World Population 2024"
fontSize: 36
fontFamily: "Inter, sans-serif"
fontWeight: "bold"
color: "#ffffff"
\`\`\`
---
## Step 7: Export
\`\`\`
pinepaper_export_svg
pinepaper_export_map
\`\`\`
---
## Variations
### Heat Map Version
\`\`\`
pinepaper_apply_data_colors
data: { ... }
options:
colorScale: "heat"
showLegend: true
\`\`\`
### Regional Focus (Asia)
\`\`\`
pinepaper_pan_map lat: 35 lon: 100 animate: true duration: 2
pinepaper_zoom_map level: 2.5 animate: true
\`\`\`
`,
'pinepaper://examples/us-election-map': `# US Election Map Example
Build an interactive US election results visualization with state-by-state coloring and key battleground state highlights.
## Final Result
A classic red/blue election map with swing states highlighted and electoral vote counts.
---
## Step 1: Set Up Canvas
\`\`\`
pinepaper_set_background_color color: "#f8fafc"
pinepaper_set_canvas_size preset: "presentation-16x9"
\`\`\`
---
## Step 2: Load USA Map
\`\`\`
pinepaper_load_map mapId: "usa"
options:
projection: "albers"
quality: "professional"
fillColor: "#e2e8f0"
strokeColor: "#94a3b8"
strokeWidth: 1
enableHover: true
enableClick: true
\`\`\`
---
## Step 3: Color Democratic States (Blue)
\`\`\`
pinepaper_highlight_regions
regionIds: ["CA", "WA", "OR", "NV", "CO", "NM", "MN", "IL", "VA", "MD", "DE", "NJ", "CT", "RI", "MA", "VT", "NH", "ME", "NY", "HI"]
options:
fillColor: "#2563eb"
strokeColor: "#1d4ed8"
\`\`\`
---
## Step 4: Color Republican States (Red)
\`\`\`
pinepaper_highlight_regions
regionIds: ["TX", "OK", "KS", "NE", "SD", "ND", "MT", "WY", "ID", "UT", "AZ", "AK", "MO", "AR", "LA", "MS", "AL", "TN", "KY", "WV", "IN", "OH", "SC", "FL"]
options:
fillColor: "#dc2626"
strokeColor: "#b91c1c"
\`\`\`
---
## Step 5: Highlight Swing States
\`\`\`
// Key battleground states with gold border
pinepaper_highlight_regions
regionIds: ["PA", "MI", "WI", "GA", "NC", "AZ"]
options:
strokeColor: "#fbbf24"
strokeWidth: 3
animate: true
\`\`\`
---
## Step 6: Add Electoral Vote Labels
\`\`\`
// California - 54 votes
pinepaper_add_marker lat: 36.7783 lon: -119.4179
label: "54"
color: "#2563eb"
size: 12
// Texas - 40 votes
pinepaper_add_marker lat: 31.9686 lon: -99.9018
label: "40"
color: "#dc2626"
size: 12
// Florida - 30 votes
pinepaper_add_marker lat: 27.6648 lon: -81.5158
label: "30"
color: "#dc2626"
size: 10
// New York - 28 votes
pinepaper_add_marker lat: 42.1657 lon: -74.9481
label: "28"
color: "#2563eb"
size: 10
// Pennsylvania - 19 votes (swing)
pinepaper_add_marker lat: 41.2033 lon: -77.1945
label: "19"
color: "#fbbf24"
size: 10
pulse: true
// Michigan - 15 votes (swing)
pinepaper_add_marker lat: 44.3148 lon: -85.6024
label: "15"
color: "#fbbf24"
size: 9
pulse: true
// Georgia - 16 votes (swing)
pinepaper_add_marker lat: 32.1656 lon: -82.9001
label: "16"
color: "#fbbf24"
size: 9
pulse: true
\`\`\`
---
## Step 7: Add Title and Legend
\`\`\`
// Title
pinepaper_create_item itemType: "text"
position: {x: 640, y: 40}
properties:
content: "US Presidential Election Results"
fontSize: 32
fontWeight: "bold"
color: "#1e293b"
// Legend - Democratic
pinepaper_create_item itemType: "rectangle"
position: {x: 100, y: 550}
properties:
width: 20
height: 20
color: "#2563eb"
pinepaper_create_item itemType: "text"
position: {x: 130, y: 550}
properties:
content: "Democratic"
fontSize: 14
color: "#1e293b"
// Legend - Republican
pinepaper_create_item itemType: "rectangle"
position: {x: 250, y: 550}
properties:
width: 20
height: 20
color: "#dc2626"
pinepaper_create_item itemType: "text"
position: {x: 280, y: 550}
properties:
content: "Republican"
fontSize: 14
color: "#1e293b"
// Legend - Swing State
pinepaper_create_item itemType: "rectangle"
position: {x: 400, y: 550}
properties:
width: 20
height: 20
color: "#fbbf24"
pinepaper_create_item itemType: "text"
position: {x: 430, y: 550}
properties:
content: "Swing State"
fontSize: 14
color: "#1e293b"
\`\`\`
---
## Step 8: Add Vote Totals
\`\`\`
// Electoral vote boxes
pinepaper_create_item itemType: "rectangle"
position: {x: 200, y: 100}
properties:
width: 150
height: 80
color: "#2563eb"
cornerRadius: 8
pinepaper_create_item itemType: "text"
position: {x: 200, y: 90}
properties:
content: "270"
fontSize: 36
fontWeight: "bold"
color: "#ffffff"
pinepaper_create_item itemType: "text"
position: {x: 200, y: 120}
properties:
content: "Electoral Votes"
fontSize: 12
color: "#bfdbfe"
// Republican votes
pinepaper_create_item itemType: "rectangle"
position: {x: 1080, y: 100}
properties:
width: 150
height: 80
color: "#dc2626"
cornerRadius: 8
pinepaper_create_item itemType: "text"
position: {x: 1080, y: 90}
properties:
content: "268"
fontSize: 36
fontWeight: "bold"
color: "#ffffff"
pinepaper_create_item itemType: "text"
position: {x: 1080, y: 120}
properties:
content: "Electoral Votes"
fontSize: 12
color: "#fecaca"
\`\`\`
---
## Step 9: Export
\`\`\`
pinepaper_export_svg
\`\`\`
`,
'pinepaper://examples/travel-route-map': `# Travel Route Map Example
Visualize a travel itinerary across Europe with city markers, connecting routes, and animated journey highlights.
## Final Result
An elegant travel map showing a European tour with cities connected by animated paths.
---
## Step 1: Set Up Canvas
\`\`\`
pinepaper_set_background_color color: "#0f172a"
pinepaper_set_canvas_size preset: "presentation-16x9"
\`\`\`
---
## Step 2: Load World Map (Focused on Europe)
\`\`\`
pinepaper_load_map mapId: "worldHighRes"
options:
projection: "mercator"
quality: "professional"
fillColor: "#1e293b"
strokeColor: "#334155"
strokeWidth: 0.5
center: [10, 50]
enableHover: true
\`\`\`
---
## Step 3: Zoom to Europe
\`\`\`
pinepaper_pan_map lat: 48 lon: 10 animate: true duration: 1
pinepaper_zoom_map level: 4 animate: true duration: 1
\`\`\`
---
## Step 4: Highlight Countries on Itinerary
\`\`\`
pinepaper_highlight_regions
regionIds: ["United Kingdom", "France", "Germany", "Italy", "Spain"]
options:
fillColor: "#334155"
strokeColor: "#60a5fa"
strokeWidth: 1
animate: true
\`\`\`
---
## Step 5: Add City Markers (The Journey)
\`\`\`
// Day 1-3: London
pinepaper_add_marker lat: 51.5074 lon: -0.1278
label: "London"
color: "#f59e0b"
size: 12
shape: "pin"
pulse: true
// Day 4-5: Paris
pinepaper_add_marker lat: 48.8566 lon: 2.3522
label: "Paris"
color: "#f59e0b"
size: 12
shape: "pin"
pulse: true
// Day 6-7: Berlin
pinepaper_add_marker lat: 52.5200 lon: 13.4050
label: "Berlin"
color: "#f59e0b"
size: 12
shape: "pin"
pulse: true
// Day 8-10: Rome
pinepaper_add_marker lat: 41.9028 lon: 12.4964
label: "Rome"
color: "#f59e0b"
size: 12
shape: "pin"
pulse: true
// Day 11-12: Barcelona
pinepaper_add_marker lat: 41.3851 lon: 2.1734
label: "Barcelona"
color: "#f59e0b"
size: 12
shape: "pin"
pulse: true
\`\`\`
---
## Step 6: Draw Route Lines
\`\`\`
// London to Paris
pinepaper_create_item itemType: "line"
properties:
from: {x: 380, y: 180}
to: {x: 420, y: 220}
strokeColor: "#60a5fa"
strokeWidth: 2
dashArray: [5, 5]
→ route1
// Paris to Berlin
pinepaper_create_item itemType: "line"
properties:
from: {x: 420, y: 220}
to: {x: 520, y: 180}
strokeColor: "#60a5fa"
strokeWidth: 2
dashArray: [5, 5]
→ route2
// Berlin to Rome
pinepaper_create_item itemType: "line"
properties:
from: {x: 520, y: 180}
to: {x: 500, y: 340}
strokeColor: "#60a5fa"
strokeWidth: 2
dashArray: [5, 5]
→ route3
// Rome to Barcelona
pinepaper_create_item itemType: "line"
properties:
from: {x: 500, y: 340}
to: {x: 380, y: 330}
strokeColor: "#60a5fa"
strokeWidth: 2
dashArray: [5, 5]
→ route4
\`\`\`
---
## Step 7: Animate Routes
\`\`\`
pinepaper_animate_item itemId: "route1" animationType: "pulse" speed: 2
pinepaper_animate_item itemId: "route2" animationType: "pulse" speed: 2
pinepaper_animate_item itemId: "route3" animationType: "pulse" speed: 2
pinepaper_animate_item itemId: "route4" animationType: "pulse" speed: 2
\`\`\`
---
## Step 8: Add Day Numbers
\`\`\`
// London days
pinepaper_create_item itemType: "text"
position: {x: 360, y: 160}
properties:
content: "Days 1-3"
fontSize: 10
color: "#94a3b8"
// Paris days
pinepaper_create_item itemType: "text"
position: {x: 440, y: 235}
properties:
content: "Days 4-5"
fontSize: 10
color: "#94a3b8"
// Berlin days
pinepaper_create_item itemType: "text"
position: {x: 540, y: 165}
properties:
content: "Days 6-7"
fontSize: 10
color: "#94a3b8"
// Rome days
pinepaper_create_item itemType: "text"
position: {x: 520, y: 355}
properties:
content: "Days 8-10"
fontSize: 10
color: "#94a3b8"
// Barcelona days
pinepaper_create_item itemType: "text"
position: {x: 360, y: 345}
properties:
content: "Days 11-12"
fontSize: 10
color: "#94a3b8"
\`\`\`
---
## Step 9: Add Title and Decorations
\`\`\`
// Title
pinepaper_create_item itemType: "text"
position: {x: 640, y: 40}
properties:
content: "European Adventure 2024"
fontSize: 28
fontWeight: "bold"
color: "#f8fafc"
// Subtitle
pinepaper_create_item itemType: "text"
position: {x: 640, y: 70}
properties:
content: "12 Days • 5 Cities • 1 Amazing Journey"
fontSize: 14
color: "#94a3b8"
// Airplane icon at start
pinepaper_create_item itemType: "text"
position: {x: 340, y: 180}
properties:
content: "✈️"
fontSize: 20
\`\`\`
---
## Step 10: Add Summary Box
\`\`\`
// Summary background
pinepaper_create_item itemType: "rectangle"
position: {x: 1100, y: 300}
properties:
width: 180
height: 200
color: "#1e293b"
cornerRadius: 12
strokeColor: "#334155"
strokeWidth: 1
// Summary title
pinepaper_create_item itemType: "text"
position: {x: 1100, y: 230}
properties:
content: "Trip Summary"
fontSize: 14
fontWeight: "bold"
color: "#f8fafc"
// Stats
pinepaper_create_item itemType: "text"
position: {x: 1100, y: 260}
properties:
content: "📍 5 Cities"
fontSize: 12
color: "#94a3b8"
pinepaper_create_item itemType: "text"
position: {x: 1100, y: 285}
properties:
content: "📅 12 Days"
fontSize: 12
color: "#94a3b8"
pinepaper_create_item itemType: "text"
position: {x: 1100, y: 310}
properties:
content: "🛫 4 Flights"
fontSize: 12
color: "#94a3b8"
pinepaper_create_item itemType: "text"
position: {x: 1100, y: 335}
properties:
content: "🏨 11 Nights"
fontSize: 12
color: "#94a3b8"
\`\`\`
---
## Step 11: Export
\`\`\`
pinepaper_export_svg
\`\`\`
---
## Variations
### Animated Route Reveal
Use keyframe animations to reveal the route step by step:
\`\`\`
pinepaper_keyframe_animate itemId: "route1"
keyframes:
- time: 0, properties: {opacity: 0}
- time: 1, properties: {opacity: 1}
pinepaper_keyframe_animate itemId: "route2"
keyframes:
- time: 1, properties: {opacity: 0}
- time: 2, properties: {opacity: 1}
\`\`\`
### Distance Labels
Add distance between cities:
\`\`\`
pinepaper_create_item itemType: "text"
position: {x: 400, y: 200}
properties:
content: "344 km"
fontSize: 8
color: "#60a5fa"
\`\`\`
`,
'pinepaper://docs/diagrams': `# Diagrams Guide
**Intent:** Create flowcharts, UML diagrams, network architectures, and connect any visual elements with smart arrows.
## When to Use Diagrams
Use diagram tools when you need to:
- Build flowcharts with decision logic
- Create UML diagrams (class, use case)
- Design network/architecture diagrams
- Connect any items (images, shapes, text) with arrows
- Show process flows with animated connectors
## Key Tools
| Tool | Purpose |
|------|---------|
| \`pinepaper_create_diagram_shape\` | Create flowchart/UML/network shapes |
| \`pinepaper_connect\` | Connect items with smart arrows |
| \`pinepaper_auto_layout\` | Automatically arrange connected shapes |
| \`pinepaper_get_diagram_shapes\` | List available shape types |
| \`pinepaper_diagram_mode\` | Enable/disable diagram editing mode |
## Shape Types
### Flowchart Shapes
| Shape | Use For |
|-------|---------|
| \`process\` | Actions, steps (rectangle) |
| \`decision\` | Yes/No branching (diamond) |
| \`terminal\` | Start/End points (rounded) |
| \`data\` | Input/Output (parallelogram) |
| \`document\` | Documents (wavy bottom) |
| \`database\` | Data storage (cylinder) |
| \`preparation\` | Setup steps (hexagon) |
### UML Shapes
| Shape | Use For |
|-------|---------|
| \`uml-class\` | Class definitions |
| \`uml-usecase\` | Use case ellipse |
| \`uml-actor\` | Stick figure actor |
### Network Shapes
| Shape | Use For |
|-------|---------|
| \`cloud\` | Cloud services |
| \`server\` | Server/computer |
## Creating a Flowchart
\`\`\`
// Step 1: Create shapes
pinepaper_create_diagram_shape
shapeType: "terminal"
position: {x: 400, y: 100}
label: "Start"
style: {fillColor: "#22c55e"}
→ item_1
pinepaper_create_diagram_shape
shapeType: "process"
position: {x: 400, y: 200}
label: "Process Data"
→ item_2
pinepaper_create_diagram_shape
shapeType: "decision"
position: {x: 400, y: 320}
label: "Valid?"
style: {fillColor: "#fbbf24"}
→ item_3
pinepaper_create_diagram_shape
shapeType: "terminal"
position: {x: 400, y: 440}
label: "End"
style: {fillColor: "#ef4444"}
→ item_4
// Step 2: Connect shapes
pinepaper_connect
sourceItemId: "item_1"
targetItemId: "item_2"
routing: "orthogonal"
pinepaper_connect
sourceItemId: "item_2"
targetItemId: "item_3"
pinepaper_connect
sourceItemId: "item_3"
targetItemId: "item_4"
label: "Yes"
\`\`\`
## Connector Options
### Routing Styles
| Style | Description |
|-------|-------------|
| \`orthogonal\` | Right-angle paths (default, best for flowcharts) |
| \`curved\` | Smooth curved paths |
| \`direct\` | Straight line |
### Arrow Styles
| Style | Description |
|-------|-------------|
| \`classic\` | Standard arrow (default) |
| \`stealth\` | Sleek pointed arrow |
| \`sharp\` | Sharp angular arrow |
| \`open\` | Outlined arrow |
| \`diamond\` | Diamond shape |
| \`circle\` | Circle endpoint |
| \`none\` | No arrowhead |
### Line Styles
| Style | Description |
|-------|-------------|
| \`solid\` | Continuous line |
| \`dashed\` | Dashed line |
| \`dotted\` | Dotted line |
## Connector Example
\`\`\`
pinepaper_connect
sourceItemId: "item_1"
targetItemId: "item_2"
routing: "curved"
lineColor: "#3b82f6"
lineWidth: 2
lineStyle: "dashed"
headStyle: "stealth"
label: "Yes"
animated: true // Bolt effect shows direction
\`\`\`
## Connecting Any Items
Diagram connectors work with ANY canvas items, not just diagram shapes:
\`\`\`
// Connect images
pinepaper_import_svg url: "https://example.com/icon1.svg" → item_1
pinepaper_import_svg url: "https://example.com/icon2.svg" → item_2
pinepaper_connect sourceItemId: "item_1" targetItemId: "item_2"
// Connect text labels
pinepaper_create_item itemType: "text" properties: {content: "A"} → item_3
pinepaper_create_item itemType: "text" properties: {content: "B"} → item_4
pinepaper_connect sourceItemId: "item_3" targetItemId: "item_4"
\`\`\`
## Auto Layout
Automatically arrange connected shapes:
\`\`\`
pinepaper_auto_layout
direction: "TB" // TB, BT, LR, RL
spacing: {horizontal: 80, vertical: 60}
\`\`\`
| Direction | Description |
|-----------|-------------|
| \`TB\` | Top to Bottom |
| \`BT\` | Bottom to Top |
| \`LR\` | Left to Right |
| \`RL\` | Right to Left |
`,
'pinepaper://docs/masks': `# Masks Guide
**Intent:** Create reveal animations and transitions using animated clipping masks (wipes, iris, curtains, custom shapes).
## When to Use Masks
Use mask tools when you need to:
- Reveal text or images with wipe effects
- Create iris/zoom reveal animations
- Build curtain-style transitions
- Design custom reveal sequences
- Add cinematic letterbox effects
## Key Tools
| Tool | Purpose |
|------|---------|
| \`pinepaper_apply_animated_mask\` | Apply reveal animation (preset or custom) |
| \`pinepaper_apply_custom_mask\` | Full keyframe control |
| \`pinepaper_remove_mask\` | Remove mask from item |
| \`pinepaper_get_mask_animations\` | List available presets |
| \`pinepaper_get_mask_types\` | List mask shapes |
| \`pinepaper_get_animatable_properties\` | Get keyframeable properties |
| \`pinepaper_get_available_easings\` | List easing functions |
## Animation Presets
### Wipe Effects
| Preset | Effect |
|--------|--------|
| \`wipeLeft\` | Reveal from left to right |
| \`wipeRight\` | Reveal from right to left |
| \`wipeUp\` | Reveal from bottom to top |
| \`wipeDown\` | Reveal from top to bottom |
| \`diagonalWipe\` | Angled reveal from corner |
### Iris Effects
| Preset | Effect |
|--------|--------|
| \`iris\` | Circle expands from center |
| \`irisOut\` | Circle shrinks to center (hide) |
### Shape Reveals
| Preset | Effect |
|--------|--------|
| \`star\` | Star shape scales up |
| \`heart\` | Heart shape scales up |
### Curtain Effects
| Preset | Effect |
|--------|--------|
| \`curtainHorizontal\` | Opens from center horizontally |
| \`curtainVertical\` | Opens from center vertically |
| \`cinematic\` | Letterbox bars animate away |
### Text Reveals
| Preset | Effect |
|--------|--------|
| \`revealUp\` | Text slides up within mask |
| \`revealDown\` | Text slides down within mask |
## Quick Start: Preset Mode
\`\`\`
// Create an item
pinepaper_create_item
itemType: "text"
position: {x: 400, y: 300}
properties: {content: "Hello World", fontSize: 48, color: "#ffffff"}
→ item_1
// Apply wipe reveal
pinepaper_apply_animated_mask
itemId: "item_1"
preset: "wipeLeft"
options:
duration: 0.5
easing: "easeOut"
\`\`\`
## Preset Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| \`startTime\` | number | 0 | Delay before animation starts (seconds) |
| \`duration\` | number | 0.8 | Animation duration (seconds) |
| \`easing\` | string | "easeOut" | Timing function |
| \`reversed\` | boolean | false | Play in reverse (hide instead of reveal) |
| \`loop\` | boolean | false | Loop the animation |
## Easing Functions
| Easing | Description |
|--------|-------------|
| \`linear\` | Constant speed |
| \`easeIn\` | Slow start, fast end |
| \`easeOut\` | Fast start, slow end |
| \`easeInOut\` | Slow start and end |
| \`bounce\` | Bouncing effect at end |
| \`elastic\` | Spring-like overshoot |
## Custom Keyframe Animation
For full control, define keyframes manually:
\`\`\`
pinepaper_apply_animated_mask
itemId: "item_1"
maskType: "rectangle"
keyframes:
- time: 0
properties: {x: 200, y: 250, width: 0, height: 100}
easing: "linear"
- time: 0.5
properties: {x: 200, y: 250, width: 100, height: 100}
easing: "easeIn"
- time: 1
properties: {x: 200, y: 250, width: 200, height: 100}
easing: "easeOut"
\`\`\`
## Mask Types
| Type | Animatable Properties |
|------|----------------------|
| \`rectangle\` | x, y, width, height, rotation, opacity |
| \`circle\` | x, y, radius, scale, opacity |
| \`ellipse\` | x, y, radiusX, radiusY, rotation, scale, opacity |
| \`star\` | x, y, radius, scale, rotation, opacity |
| \`triangle\` | x, y, scale, rotation, opacity |
| \`hexagon\` | x, y, scale, rotation, opacity |
| \`heart\` | x, y, scale, rotation, opacity |
| \`rounded\` | x, y, width, height, cornerRadius, opacity |
## Hybrid Mode: Preset + Custom Keyframes
Combine preset behavior with custom timing:
\`\`\`
pinepaper_apply_animated_mask
itemId: "item_1"
preset: "iris"
keyframes:
- time: 0
properties: {scale: 0.01}
easing: "linear"
- time: 0.3
properties: {scale: 0.3}
easing: "easeIn"
- time: 0.7
properties: {scale: 0.8}
easing: "easeOut"
- time: 1
properties: {scale: 1}
easing: "bounce"
\`\`\`
## Staggered Text Reveal
Reveal multiple words with delays:
\`\`\`
// Create words
pinepaper_create_item itemType: "text" position: {x: 200, y: 300}
properties: {content: "Hello", fontSize: 36} → item_1
pinepaper_create_item itemType: "text" position: {x: 350, y: 300}
properties: {content: "Beautiful", fontSize: 36} → item_2
pinepaper_create_item itemType: "text" position: {x: 550, y: 300}
properties: {content: "World", fontSize: 36} → item_3
// Apply staggered reveals
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeUp"
options: {startTime: 0, duration: 0.3}
pinepaper_apply_animated_mask itemId: "item_2" preset: "wipeUp"
options: {startTime: 0.2, duration: 0.3}
pinepaper_apply_animated_mask itemId: "item_3" preset: "wipeUp"
options: {startTime: 0.4, duration: 0.3}
\`\`\`
## Removing Masks
\`\`\`
pinepaper_remove_mask itemId: "item_1"
\`\`\`
`,
'pinepaper://docs/fonts': `# Font Studio Guide
**Intent:** Create custom hand-drawn fonts by drawing glyphs on a dedicated canvas, then export as OTF files or use directly in PinePaper documents.
## Overview
Font Studio provides 16 tools for creating custom fonts programmatically. You draw each character as a Paper.js path, clean it up, and convert it to a font glyph.
## Key Tools
| Tool | Purpose |
|------|---------|
| \`pinepaper_font_show_studio\` | Open the Font Studio UI with guide lines |
| \`pinepaper_font_set_name\` | Set font family name |
| \`pinepaper_font_get_required_chars\` | Get list of characters needed |
| \`pinepaper_font_get_status\` | Check completion progress |
| \`pinepaper_font_create_glyph\` | Convert a drawn path to a glyph |
| \`pinepaper_font_create_space\` | Create the space character |
| \`pinepaper_font_remove_glyph\` | Remove a glyph for redrawing |
| \`pinepaper_font_set_metrics\` | Configure font metrics (advanced) |
| \`pinepaper_font_export\` | Export as OTF file |
| \`pinepaper_font_load_into_document\` | Use font in current document |
| \`pinepaper_font_export_data\` | Save progress as JSON |
| \`pinepaper_font_import_data\` | Resume from saved data |
| \`pinepaper_font_clear\` | Reset all glyphs |
| \`pinepaper_font_remove_overlap\` | Merge overlapping path strokes |
| \`pinepaper_font_correct_direction\` | Fix path winding for fills |
| \`pinepaper_font_cleanup_path\` | All-in-one path preparation |
## Workflow
### Step 1: Open Font Studio
\`\`\`
pinepaper_font_show_studio
\`\`\`
Opens the interactive Font Studio UI with typography guide lines:
- **Cap Height** (Red): Top of capital letters (H, T, A)
- **x-Height** (Cyan): Top of lowercase letters (a, e, x)
- **Baseline** (Green): Where letters sit
- **Descender** (Purple): Below baseline for g, p, y, j
### Step 2: Set Font Name
\`\`\`
pinepaper_font_set_name name: "MyHandwriting"
\`\`\`
### Step 3: Check Required Characters
\`\`\`
pinepaper_font_get_required_chars set: "minimum"
\`\`\`
Two character sets available:
- **minimum**: A-Z, a-z, 0-9, space (63 chars) - enough for basic text
- **standard**: adds punctuation and symbols (90+ chars)
### Step 4: Draw and Create Glyphs
For each character:
1. **Draw the character** as a Paper.js path on the canvas
2. **Clean up the path** (recommended):
\`\`\`
pinepaper_font_cleanup_path pathId: "item_42"
\`\`\`
3. **Create the glyph**:
\`\`\`
pinepaper_font_create_glyph character: "A" pathId: "item_42"
\`\`\`
For the space character (no visible path needed):
\`\`\`
pinepaper_font_create_space width: 250
\`\`\`
### Step 5: Check Progress
\`\`\`
pinepaper_font_get_status
\`\`\`
Returns: font name, total/completed/pending counts, percentage, and character lists.
### Step 6: Export
Export as OTF file:
\`\`\`
pinepaper_font_export
\`\`\`
Or load directly into the document:
\`\`\`
pinepaper_font_load_into_document
\`\`\`
## Path Cleanup Tools
Before converting a drawn path to a glyph, clean it up for best results:
| Tool | What It Does |
|------|-------------|
| \`pinepaper_font_remove_overlap\` | Merges overlapping strokes into one outline |
| \`pinepaper_font_correct_direction\` | Fixes winding so fills render correctly |
| \`pinepaper_font_cleanup_path\` | Does all cleanup in one call (overlap + direction + smooth) |
The \`cleanup_path\` tool accepts options:
\`\`\`
pinepaper_font_cleanup_path
pathId: "item_42"
removeOverlap: true
correctDirection: true
smooth: true
smoothTolerance: 2.5
\`\`\`
## Font Metrics (Advanced)
Configure the font's coordinate system:
\`\`\`
pinepaper_font_set_metrics
unitsPerEm: 1000
ascender: 800
descender: -200
xHeight: 500
capHeight: 700
\`\`\`
## Save and Resume Progress
Save work-in-progress:
\`\`\`
pinepaper_font_export_data
\`\`\`
Resume later:
\`\`\`
pinepaper_font_import_data data: { ... }
\`\`\`
## Best Practices
- Keep stroke width consistent across all characters
- Uppercase letters should touch the cap height line
- Lowercase (except b, d, f, h, k, l, t) stay within x-height
- Letters g, j, p, q, y extend below baseline to descender
- Always run \`cleanup_path\` before \`create_glyph\` for cleanest results
- Use \`get_status\` frequently to track progress
- Save progress with \`export_data\` periodically
`,
'pinepaper://examples/us-states-animated': `# US States Animated Map Example
**Intent:** Create a US map with animated color transitions that loop continuously.
## What You'll Build
A USA map where states randomly change colors in a flowing wave animation.
## Step 1: Load the Map
\`\`\`
pinepaper_load_map
mapId: "usa"
options:
projection: "albers"
quality: "balanced"
fillColor: "#e5e7eb"
strokeColor: "#9ca3af"
\`\`\`
## Step 2: Apply Wave Animation
\`\`\`
pinepaper_animate_map_wave
duration: 10
loop: true
colors: ["#ef4444", "#f97316", "#fbbf24", "#22c55e", "#3b82f6", "#8b5cf6"]
options:
delayBetween: 0.1
easing: "easeInOut"
\`\`\`
## Result
States will animate through the color palette in a wave pattern, creating a mesmerizing continuous animation.
## Alternative: Data-Driven Colors
Apply colors based on data values:
\`\`\`
pinepaper_apply_data_colors
data:
"CA": 100
"TX": 85
"FL": 70
"NY": 65
"PA": 50
"IL": 45
"OH": 40
options:
colorScale: "blues"
legend: true
legendPosition: "bottom-right"
\`\`\`
## Alternative: Highlight Specific States
\`\`\`
pinepaper_highlight_regions
regionIds: ["CA", "OR", "WA"]
options:
fillColor: "#22c55e"
strokeColor: "#16a34a"
animated: true
\`\`\`
## Key Tools Used
| Tool | Purpose |
|------|---------|
| \`pinepaper_load_map\` | Load USA map with Albers projection |
| \`pinepaper_animate_map_wave\` | Create looping color animation |
| \`pinepaper_apply_data_colors\` | Color by numeric values |
| \`pinepaper_highlight_regions\` | Highlight specific states |
## Region ID Format
For USA maps, use 2-letter state codes:
- CA, TX, NY, FL, WA, OR, etc.
## Export
\`\`\`
pinepaper_export_svg animated: true
\`\`\`
`,
'pinepaper://examples/flowchart': `# Flowchart Example
**Intent:** Create a decision flowchart with auto-layout and animated connectors.
## What You'll Build
A simple decision flowchart: Start → Process → Decision → End
## Step 1: Create Shapes
\`\`\`
// Start terminal
pinepaper_create_diagram_shape
shapeType: "terminal"
position: {x: 400, y: 80}
label: "Start"
style: {fillColor: "#22c55e", strokeColor: "#16a34a"}
→ item_1
// Process step
pinepaper_create_diagram_shape
shapeType: "process"
position: {x: 400, y: 180}
label: "Check Input"
style: {fillColor: "#3b82f6", strokeColor: "#2563eb"}
→ item_2
// Decision diamond
pinepaper_create_diagram_shape
shapeType: "decision"
position: {x: 400, y: 300}
label: "Valid?"
style: {fillColor: "#fbbf24", strokeColor: "#f59e0b"}
→ item_3
// Success path
pinepaper_create_diagram_shape
shapeType: "process"
position: {x: 250, y: 420}
label: "Process Data"
style: {fillColor: "#3b82f6", strokeColor: "#2563eb"}
→ item_4
// Error path
pinepaper_create_diagram_shape
shapeType: "process"
position: {x: 550, y: 420}
label: "Show Error"
style: {fillColor: "#ef4444", strokeColor: "#dc2626"}
→ item_5
// End terminal
pinepaper_create_diagram_shape
shapeType: "terminal"
position: {x: 400, y: 540}
label: "End"
style: {fillColor: "#6b7280", strokeColor: "#4b5563"}
→ item_6
\`\`\`
## Step 2: Connect Shapes
\`\`\`
// Start to Check
pinepaper_connect
sourceItemId: "item_1"
targetItemId: "item_2"
routing: "orthogonal"
lineColor: "#374151"
// Check to Decision
pinepaper_connect
sourceItemId: "item_2"
targetItemId: "item_3"
routing: "orthogonal"
lineColor: "#374151"
// Decision Yes path
pinepaper_connect
sourceItemId: "item_3"
targetItemId: "item_4"
routing: "orthogonal"
lineColor: "#22c55e"
label: "Yes"
// Decision No path
pinepaper_connect
sourceItemId: "item_3"
targetItemId: "item_5"
routing: "orthogonal"
lineColor: "#ef4444"
label: "No"
// Process to End
pinepaper_connect
sourceItemId: "item_4"
targetItemId: "item_6"
routing: "orthogonal"
lineColor: "#374151"
// Error to End
pinepaper_connect
sourceItemId: "item_5"
targetItemId: "item_6"
routing: "orthogonal"
lineColor: "#374151"
\`\`\`
## Step 3: Apply Auto-Layout (Optional)
\`\`\`
pinepaper_auto_layout
direction: "TB"
spacing: {horizontal: 100, vertical: 80}
\`\`\`
## Adding Animation
Make connectors animated to show flow direction:
\`\`\`
pinepaper_connect
sourceItemId: "item_1"
targetItemId: "item_2"
animated: true // Bolt effect shows direction
lineColor: "#3b82f6"
\`\`\`
## Key Tools Used
| Tool | Purpose |
|------|---------|
| \`pinepaper_create_diagram_shape\` | Create flowchart symbols |
| \`pinepaper_connect\` | Draw smart connectors |
| \`pinepaper_auto_layout\` | Automatic arrangement |
## Shape Reference
| Shape | Symbol | Use For |
|-------|--------|---------|
| \`terminal\` | Rounded rectangle | Start/End |
| \`process\` | Rectangle | Actions |
| \`decision\` | Diamond | Yes/No branching |
| \`data\` | Parallelogram | Input/Output |
| \`document\` | Wavy rectangle | Documents |
`,
'pinepaper://examples/reveal-animation': `# Reveal Animation Example
**Intent:** Create a text reveal animation using mask effects.
## What You'll Build
Text that reveals with a smooth wipe animation, perfect for intros and titles.
## Method 1: Simple Preset
\`\`\`
// Create text
pinepaper_create_item
itemType: "text"
position: {x: 400, y: 300}
properties:
content: "Welcome"
fontSize: 64
fontWeight: "bold"
color: "#ffffff"
→ item_1
// Apply wipe reveal
pinepaper_apply_animated_mask
itemId: "item_1"
preset: "wipeLeft"
options:
duration: 0.6
easing: "easeOut"
\`\`\`
## Method 2: Staggered Word Reveal
\`\`\`
// Word 1
pinepaper_create_item itemType: "text" position: {x: 200, y: 300}
properties: {content: "Create", fontSize: 48, color: "#3b82f6"}
→ item_1
// Word 2
pinepaper_create_item itemType: "text" position: {x: 370, y: 300}
properties: {content: "Beautiful", fontSize: 48, color: "#8b5cf6"}
→ item_2
// Word 3
pinepaper_create_item itemType: "text" position: {x: 580, y: 300}
properties: {content: "Graphics", fontSize: 48, color: "#ec4899"}
→ item_3
// Staggered reveals
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeUp"
options: {startTime: 0, duration: 0.4, easing: "easeOut"}
pinepaper_apply_animated_mask itemId: "item_2" preset: "wipeUp"
options: {startTime: 0.15, duration: 0.4, easing: "easeOut"}
pinepaper_apply_animated_mask itemId: "item_3" preset: "wipeUp"
options: {startTime: 0.3, duration: 0.4, easing: "easeOut"}
\`\`\`
## Method 3: Iris Reveal for Images
\`\`\`
// Create a shape or import image
pinepaper_create_item itemType: "circle" position: {x: 400, y: 300}
properties: {radius: 100, color: "#3b82f6"}
→ item_1
// Iris reveal from center
pinepaper_apply_animated_mask
itemId: "item_1"
preset: "iris"
options:
duration: 0.8
easing: "easeOut"
\`\`\`
## Method 4: Cinematic Letterbox
\`\`\`
// Create main content
pinepaper_create_item itemType: "rectangle" position: {x: 400, y: 300}
properties: {width: 600, height: 300, color: "#1e40af"}
→ item_1
// Cinematic reveal (letterbox bars animate away)
pinepaper_apply_animated_mask
itemId: "item_1"
preset: "cinematic"
options:
duration: 1.2
easing: "easeInOut"
\`\`\`
## Available Presets
| Preset | Effect | Best For |
|--------|--------|----------|
| \`wipeLeft\` | Left to right | Text, titles |
| \`wipeRight\` | Right to left | Text |
| \`wipeUp\` | Bottom to top | Credits, lists |
| \`wipeDown\` | Top to bottom | Headers |
| \`iris\` | Circle from center | Images, focus |
| \`irisOut\` | Circle shrinks | Transitions out |
| \`star\` | Star shape reveal | Fun, playful |
| \`heart\` | Heart shape reveal | Love themes |
| \`curtainHorizontal\` | Opens from center | Stage, dramatic |
| \`curtainVertical\` | Opens vertically | Reveals |
| \`cinematic\` | Letterbox effect | Film style |
| \`diagonalWipe\` | Angled wipe | Dynamic |
## Combining with Other Animations
\`\`\`
// Create text
pinepaper_create_item itemType: "text" position: {x: 400, y: 300}
properties: {content: "Animated!", fontSize: 48}
→ item_1
// Apply mask reveal
pinepaper_apply_animated_mask itemId: "item_1" preset: "wipeLeft"
options: {duration: 0.5}
// Add pulse animation after reveal
pinepaper_animate_item itemId: "item_1" animationType: "pulse" speed: 0.5
\`\`\`
## Key Tools
| Tool | Purpose |
|------|---------|
| \`pinepaper_apply_animated_mask\` | Apply reveal preset |
| \`pinepaper_get_mask_animations\` | List all presets |
| \`pinepaper_remove_mask\` | Remove mask effect |
`,
};
// =============================================================================
// SERVER OPTIONS
// =============================================================================
export interface ServerOptions {
locale?: SupportedLocale;
/** Enable browser execution mode - executes code in real browser via Puppeteer */
browserMode?: boolean;
/** Execution mode: 'puppeteer' (default) or 'code' (generate only for manual paste) */
executionMode?: ExecutionMode;
/** Tool description verbosity: 'verbose', 'compact', or 'minimal' */
verbosity?: ToolVerbosity;
/** Toolkit profile: controls which tools are exposed via tools/list */
toolkit?: ToolkitProfile;
}
// =============================================================================
// SERVER CREATION
// =============================================================================
export async function createServer(options: ServerOptions = {}): Promise<Server> {
// Initialize i18n with specified locale
const locale = options.locale || detectLocaleFromEnvironment();
const i18n = await initI18n(locale);
// Mutable effective settings — may be upgraded by client auto-detection
let effectiveVerbosity: ToolVerbosity = options.verbosity || detectVerbosityFromEnvironment();
let effectiveToolkit: ToolkitProfile = options.toolkit || detectToolkitFromEnvironment();
/** Compute the current filtered tools list (sub-ms, called per tools/list request). */
const getEffectiveTools = () => getToolsForToolkit(getToolsForVerbosity(effectiveVerbosity), effectiveToolkit);
const server = new Server(SERVER_INFO, {
capabilities: {
tools: { listChanged: true },
resources: {},
prompts: {},
},
});
// Client auto-detection: upgrade toolkit/verbosity based on MCP client identity
server.oninitialized = () => {
const clientVersion = server.getClientVersion();
if (!clientVersion?.name) return;
const profile = getClientProfile(clientVersion.name);
if (!profile) return;
let changed = false;
// Only upgrade if env vars / CLI flags weren't explicitly set
if (!options.toolkit && !isToolkitExplicitlySet() && profile.toolkit !== effectiveToolkit) {
console.error(`[auto-select] Client "${clientVersion.name}" → toolkit=${profile.toolkit}`);
effectiveToolkit = profile.toolkit;
changed = true;
}
if (!options.verbosity && !isVerbosityExplicitlySet() && profile.verbosity !== effectiveVerbosity) {
console.error(`[auto-select] Client "${clientVersion.name}" → verbosity=${profile.verbosity}`);
effectiveVerbosity = profile.verbosity;
changed = true;
}
if (changed) {
// Notify client that tool list has changed
server.sendToolListChanged().catch(() => {});
}
};
// ---------------------------------------------------------------------------
// TOOL HANDLERS
// ---------------------------------------------------------------------------
// List available tools — dynamic, recomputed each request (sub-ms)
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: getEffectiveTools(),
};
});
// Handle tool calls with i18n support for errors and success messages
// Browser mode enables live execution in PinePaper Studio
// Execution mode: 'puppeteer' (default) or 'code' (generate only)
const executeInBrowser = options.browserMode ?? true; // Default to browser mode
const executionMode = options.executionMode ?? getExecutionMode();
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// Runtime toolkit/verbosity switching — handled here because it needs
// direct access to mutable effectiveToolkit/effectiveVerbosity state
if (name === 'pinepaper_set_toolkit') {
const { SetToolkitInputSchema } = await import('./types/schemas.js');
const { TOOLKIT_PROFILES_LIST } = await import('./tools/toolkits.js');
const input = SetToolkitInputSchema.parse(args);
let changed = false;
if (input.toolkit && input.toolkit !== effectiveToolkit) {
effectiveToolkit = input.toolkit;
changed = true;
}
if (input.verbosity && input.verbosity !== effectiveVerbosity) {
effectiveVerbosity = input.verbosity;
changed = true;
}
if (changed) {
server.sendToolListChanged().catch(() => {});
}
const tools = getEffectiveTools();
return {
content: [{
type: 'text' as const,
text: `Toolkit: ${effectiveToolkit}, Verbosity: ${effectiveVerbosity}, Tools: ${tools.length}${changed ? ' (updated)' : ' (no change)'}`,
}],
};
}
return handleToolCall(name, args as Record<string, unknown>, {
i18n,
executeInBrowser,
executionMode,
});
});
// ---------------------------------------------------------------------------
// RESOURCE HANDLERS
// ---------------------------------------------------------------------------
// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: RESOURCES,
};
});
// Read resource content
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
const content = RESOURCE_CONTENTS[uri];
if (!content) {
throw new Error(`Resource not found: ${uri}`);
}
return {
contents: [
{
uri,
mimeType: 'text/markdown',
text: content,
},
],
};
});
// ---------------------------------------------------------------------------
// PROMPT HANDLERS
// ---------------------------------------------------------------------------
server.setRequestHandler(ListPromptsRequestSchema, async () => {
return { prompts: PROMPTS };
});
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
return getPromptMessages(name, args);
});
return server;
}
// =============================================================================
// LOCALE DETECTION
// =============================================================================
/**
* Detect locale from environment variables
* Checks PINEPAPER_LOCALE, LANG, LC_ALL, LC_MESSAGES
*/
function detectLocaleFromEnvironment(): SupportedLocale {
// Check explicit PinePaper locale setting
const pinepaperLocale = process.env.PINEPAPER_LOCALE;
if (pinepaperLocale && isValidLocale(pinepaperLocale)) {
return pinepaperLocale as SupportedLocale;
}
// Check standard locale environment variables
const envLocale =
process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || '';
// Parse locale string (e.g., "en_US.UTF-8" -> "en")
const localePart = envLocale.split('.')[0]; // Remove encoding
const langPart = localePart.split('_')[0]; // Get language code
if (isValidLocale(langPart)) {
return langPart as SupportedLocale;
}
// Check for regional variants (e.g., "zh_CN" -> "zh-CN")
const regionalLocale = localePart.replace('_', '-');
if (isValidLocale(regionalLocale)) {
return regionalLocale as SupportedLocale;
}
return DEFAULT_LOCALE;
}
/**
* Check if a string is a valid supported locale
*/
function isValidLocale(locale: string): boolean {
const SUPPORTED = [
'en',
'es',
'fr',
'de',
'it',
'pt',
'pt-BR',
'nl',
'pl',
'ru',
'uk',
'zh-CN',
'zh-TW',
'ja',
'ko',
'th',
'vi',
'id',
'ms',
'tl',
'hi',
'bn',
'ta',
'te',
'mr',
'gu',
'kn',
'ml',
'pa',
'ar',
'he',
'fa',
'tr',
'sv',
'da',
'no',
'fi',
'cs',
'el',
'hu',
'ro',
];
return SUPPORTED.includes(locale);
}
// =============================================================================
// MAIN ENTRY POINT
// =============================================================================
async function main(): Promise<void> {
const locale = detectLocaleFromEnvironment();
const verbosity = detectVerbosityFromEnvironment();
const toolkit = detectToolkitFromEnvironment();
const server = await createServer({ locale, verbosity, toolkit });
const transport = new StdioServerTransport();
console.error('PinePaper MCP Server starting...');
console.error(`Version: ${SERVER_INFO.version}`);
console.error(`Locale: ${locale}`);
console.error(`Verbosity: ${verbosity}`);
console.error(`Toolkit: ${toolkit}`);
const toolCount = getToolsForToolkit(getToolsForVerbosity(verbosity), toolkit).length;
console.error(`Tools: ${toolCount}/${PINEPAPER_TOOLS.length}`);
console.error(`Resources: ${RESOURCES.length}`);
await server.connect(transport);
console.error('PinePaper MCP Server running on stdio');
}
// Run if executed directly (not when imported by cli.ts or other modules)
// Check: Node uses process.argv[1], Bun supports import.meta.main
const isDirectExecution = typeof (import.meta as any).main === 'boolean'
? (import.meta as any).main
: process.argv[1]?.replace(/\\/g, '/').endsWith('index.js');
if (isDirectExecution) {
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
}