Relation Registry
The Relation Registry manages behavior-driven associations between items.
Overview
Relations define how items interact:
orbits- circular motion around targetfollows- move toward targetattached_to- fixed offset from targetpoints_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
- LLM Fine-Tuning: Create custom models that understand PinePaper commands
- Code Completion: Train assistants to autocomplete relation code
- Natural Language Interface: Enable voice/text commands for animation
- 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);