Constellation Map Skill
Converts a mind map PNG into a physics-based interactive HTML constellation visualization.
Workflow
Follow these four steps in order. Do not skip the JSON confirmation step.
Step 1 — Parse the Image
Carefully read every node and connector in the uploaded image. Extract:
- Root node — the central/leftmost topic
- Branch nodes — direct children of root (level 1)
- Sub nodes — children of branch nodes (level 2), if present
- Leaf nodes — outermost nodes with no further children
Preserve all label text exactly as written in the image.
Output a JSON summary before writing any code:
{
"root": "Root Label",
"branches": [
{
"id": "b1",
"label": "Branch Label",
"kids": ["Leaf 1", "Leaf 2"]
},
{
"id": "b2",
"label": "Branch Label",
"subs": [
{ "id": "b2_s0", "label": "Sub Label", "kids": ["Leaf A", "Leaf B"] }
]
}
]
}
Ask the user to confirm the structure is correct before proceeding.
Step 2 — Assign Branch Colors
Assign one color slot per branch, cycling through the palette if there are more than 6:
| Slot | Fill | Glow | Label text |
|---|---|---|---|
| 1 | #c084fc |
#a855f7 |
#1a0030 |
| 2 | #34d399 |
#10b981 |
#002918 |
| 3 | #fb923c |
#f97316 |
#2d0a00 |
| 4 | #60a5fa |
#3b82f6 |
#001638 |
| 5 | #fbbf24 |
#f59e0b |
#2a1400 |
| 6 | #f87171 |
#ef4444 |
#2d0000 |
- Sub nodes: lighter tint of the branch fill
- Leaf nodes: even lighter tint
- Root node: fill
#ffffff, glow#a0c8ff, label#03060f
Step 3 — Build the HTML File
Produce a single self-contained HTML file. No external dependencies.
Canvas & Container
<div style="background:#03060f;border-radius:16px;overflow:hidden;
position:relative;user-select:none;">
<canvas id="c" style="display:block;width:100%;touch-action:none;"></canvas>
<div id="tip" style="position:absolute;pointer-events:none;display:none;
background:rgba(5,10,28,0.95);border:1px solid rgba(255,255,255,0.18);
border-radius:10px;padding:9px 14px;z-index:20;"></div>
<div style="position:absolute;bottom:10px;left:50%;transform:translateX(-50%);
color:rgba(255,255,255,0.35);font-size:11px;font-family:sans-serif;
pointer-events:none;">drag any star · click branch to focus</div>
</div>
Canvas Setup
- Size: full container width, height =
max(600px, width * 0.8) - HiDPI support via
devicePixelRatio - Background:
#03060f - 200 background stars with sine-wave twinkle animation
Node Sizes
| Type | Radius |
|---|---|
| root | 30px |
| branch | 20px |
| sub | 13px |
| leaf | 8–9px |
All non-leaf nodes get an inner bright core: rgba(255,255,255,0.35) circle at r * 0.4.
Labels
- Branch nodes: short label inside node (truncate >12 chars with
…), full label floating outside - All non-root nodes: floating label in a pill
rgba(3,6,15,0.7)background,borderRadius:4, positioned radially outward from canvas center - Hover: full opacity; non-hovered branches: 0.7 opacity; focused-out: 0.1 opacity
Edges
- Branch→root:
lineWidth 2, solid,shadowBlur 6 - Sub→branch:
lineWidth 1, solid - Leaf→parent:
lineWidth 1, dashed[4,5] - Color: branch glow color,
globalAlpha 0.55(active) or0.06(dimmed)
Node Layout
Place nodes using polar coordinates from canvas center (W/2, H/2):
- Branch nodes: evenly spaced angles
(i / numBranches) * 2π, radius~0.28 * min(W,H). Useax/ayoffsets scaled byW/900andH/700. - Sub nodes: fan from parent at
~0.26 * min(W,H)from center - Leaf nodes: fan further at
0.28–0.43 * min(W,H)depending on depth - Store
homeXandhomeYon every branch node at build time
⚠️ Critical — root node JavaScript
typemust be'center', not'root'. The Step 1 JSON schema uses the key"root"for readability, but in the generated JavaScript the root node object must be created withtype: 'center'. The physics function guards withn.type === 'center'to exempt the root from all forces. Using any other string will cause the root to drift under repulsion and break the entire layout.
Physics
Read references/physics.md for all exact constants and the full physics function implementation.
Interactions
| Action | Behavior |
|---|---|
mousedown / touchstart on node |
Begin drag, store offset |
mousemove / touchmove |
Move dragged node, zero its velocity |
mouseup with movement < 5px |
Toggle branch focus |
| Click root | Clear focus |
mouseleave |
Hide tooltip, stop drag |
touchend |
Stop drag |
⚠️ Critical — physics loop must keep running during drag. The animation loop (
requestAnimationFrame) must never pause or skip physics while a node is being dragged. The physics function already skipsn === dragging, so the dragged node is positioned directly by the mouse. All other nodes — especially leaves and subs — must continue receiving spring forces every frame so they follow the moved branch naturally. If physics stops during drag, connected leaf/sub nodes will freeze completely and not follow.
Tooltip on hover: Node label (bold 12px white) + type and parent branch name (10px, 45% white). Hidden while dragging.
Draw Order Per Frame
- Clear + fill
#03060f - Twinkling background stars
- All edges (with glow)
- Nodes sorted:
leaf → sub → branch → root(root always on top)
Step 4 — Output & Confirm
- Deliver a single
.htmlfile - Confirm all branch labels from the parsed JSON are present in the output
- Mention that branch nodes are anchored (won't drift) and leaves float freely
Quality Checklist
Before finalising, verify:
- All node labels from Step 1 JSON are present
- Branch nodes stay near their home positions (anchor spring active)
- Leaves and subs float gently without flying off screen
- Focus mode dims other branches correctly
- Tooltip shows on hover
- Drag works on mouse and touch
- Canvas resizes correctly on window resize