Relation Registry

The Relation Registry manages behavior-driven associations between items.

Overview

Relations define how items interact:

  • orbits - circular motion around target
  • follows - move toward target
  • attached_to - fixed offset from target
  • points_at - rotate to face target

Adding Relations

app.addRelation(sourceId, targetId, 'orbits', {
  radius: 100,
  speed: 0.5
});

Removing Relations

// Remove specific relation
app.removeRelation(sourceId, targetId, 'orbits');

// Remove all relations to target
app.removeRelation(sourceId, targetId);

Querying

// Get relations from item
const relations = app.getRelations(itemId);
const orbits = app.getRelations(itemId, 'orbits');

// Get items that relate TO this item
const orbiters = app.queryByRelationTarget(targetId, 'orbits');

Compositional Behavior

Relations are additive. An item can have multiple relations that compose:

// Planet orbits star
app.addRelation(planetId, starId, 'orbits', { radius: 500 });

// Planet also constrained to bounds
app.addRelation(planetId, canvasId, 'bounds_to', { padding: 20 });

// Both apply each frame!

Worker-Based Computation

Relation math runs in Web Workers for performance:

  • Heavy calculations happen off the main thread
  • Keeps animations smooth with many active relations

Animation Relations

Keyframe animations via the relation system:

// Add keyframe animation
app.relationRegistry.addKeyframeAnimation(itemId, [
  { time: 0, properties: { x: 100 }, easing: 'easeIn' },
  { time: 1, properties: { x: 400 }, easing: 'easeOut' }
], { duration: 2, loop: true });

// Chain animations
app.relationRegistry.chainAnimations(item1Id, item2Id, {
  trigger: 'onComplete',
  delay: 0.5
});

// Sync animations
app.relationRegistry.syncAnimations(item1Id, item2Id, {
  offset: 0.25,
  speedRatio: 2
});

Export/Import

// Save relations
const data = app.relationRegistry.exportForSave();

// Restore relations
app.relationRegistry.importFromSave(data);

Statistics

const stats = app.getRelationStats();
// {
//   activeItems: 5,
//   ruleCount: 8,
//   associationsByType: { orbits: 2, follows: 3 }
// }

Training Data Export

Generate instruction/code pairs for LLM fine-tuning. The training data export creates natural language descriptions paired with the corresponding PinePaper code.

Export Training Data

const trainingData = app.exportRelationTrainingData();

Output Format

Each entry contains:

Field Type Description
instruction string Natural language description
code string Corresponding JavaScript code
relation string Relation type name
params object Relation parameters

Example Output:

[
  {
    instruction: "moon orbits earth at radius 100",
    code: "app.relationRegistry.addAssociation('item_1', 'item_2', 'orbits', {\"radius\":100,\"speed\":1})",
    relation: "orbits",
    params: { radius: 100, speed: 1 }
  },
  {
    instruction: "make moon rotate around earth",
    code: "app.relationRegistry.addAssociation('item_1', 'item_2', 'orbits', {\"radius\":100,\"speed\":1})",
    relation: "orbits",
    params: { radius: 100, speed: 1 }
  },
  {
    instruction: "label follows player with offset [0, -50]",
    code: "app.relationRegistry.addAssociation('item_3', 'item_4', 'follows', {\"offset\":[0,-50],\"smoothing\":0.1})",
    relation: "follows",
    params: { offset: [0, -50], smoothing: 0.1 }
  }
]

Template System

Each relation rule has natural language templates that generate multiple training examples:

// Built-in templates for 'orbits' relation:
[
  '{item} orbits {target} at radius {radius}',
  '{item} circles around {target} with speed {speed}',
  'make {item} rotate around {target}'
]

The system replaces placeholders with actual item names and parameter values.

Custom Relation Templates

When registering custom relations, include templates for training data:

app.relationRegistry.registerRule('repels', {
  description: 'Item moves away from target',
  params: {
    force: { type: 'number', default: 50 }
  },
  compute: (ctx) => { /* ... */ },
  apply: (item, target, computed, params) => { /* ... */ },

  // Templates for training data generation
  templates: [
    '{item} repels from {target}',
    'make {item} move away from {target}',
    '{item} is repelled by {target} with force {force}'
  ]
});

Use Cases for Training Data

  1. LLM Fine-Tuning: Create custom models that understand PinePaper commands
  2. Code Completion: Train assistants to autocomplete relation code
  3. Natural Language Interface: Enable voice/text commands for animation
  4. Documentation Generation: Create examples from actual usage

Workflow for Training

// 1. Create a scene with various relations
app.addRelation(moonId, earthId, 'orbits', { radius: 100 });
app.addRelation(earthId, sunId, 'orbits', { radius: 300 });
app.addRelation(labelId, moonId, 'attached_to', { offset: [0, -20] });

// 2. Export training data
const trainingData = app.exportRelationTrainingData();

// 3. Convert to training format (e.g., JSONL for fine-tuning)
const jsonl = trainingData.map(entry => JSON.stringify({
  messages: [
    { role: 'user', content: entry.instruction },
    { role: 'assistant', content: entry.code }
  ]
})).join('\n');

// 4. Save for training
console.log(jsonl);