semantic-zoom
New in this cycle: Google-Maps-style semantic zoom — Company → Teams → Apps by clicking
Summary
Exploring hierarchical decomposition and semantic zoom—how the visualization changes at different abstraction levels, from company-wide aggregates down to individual actors.
Core Concept
Like Google Maps, the level of detail changes with scale:
| Zoom Level | Abstraction | What's Shown |
|---|---|---|
| Global | Company-wide | Aggregated traffic flows between companies |
| Regional | Team-level | Teams as containers, inter-team flows |
| Local | Individual actors | Apps, services, databases with detailed flows |
Key insight: The "inside" doesn't need to fit the "outside"—this is semantic space, not physical space. A team cube can expand to reveal 50 apps that take up more space than the original cube.
Nested Actors
Composite Pattern
Actor (base)
├── LeafActor (app, service, database, vendor)
└── CompositeActor (team, company, region...)
└── children: Actor[] ← can be leaves OR other composites
Visual States
| State | Representation |
|---|---|
| Collapsed | Simplified cube with label + child count badge |
| Expanded | Boundary outline containing positioned children |
| Leaf | Full detail mesh (dodecahedron, prism, etc. from c2) |
Container Fracturing
When drilling into a composite:
- Siblings fade/mute (context preserved but de-emphasized)
- Target cube "fractures" or splits
- Children emerge and settle into positions
- Flows re-route from aggregate to detailed endpoints
Flow Transformation
Flows adapt to the current zoom level:
- Collapsed view: Single thick line
Engineering ←→ Vendor - Expanded view: Line fans out to actual connections (
api → Vendor,auth → Vendor)
Aggregate flows "unpack" when their source/target expands.
Layout Agent
A positioning system that handles spatial arrangement dynamically.
Triggers & Responses
| Trigger | Action |
|---|---|
| Composite expands | Position children within expanded space |
| Composite collapses | Animate children back into cube |
| Sibling expands | Shift/compress to make room |
| Actor added/removed | Reflow affected region |
| Window resize | Rescale/reposition as needed |
Algorithm Options
Force-Directed Family
| Algorithm | Description | Best For |
|---|---|---|
| Spring-Embedder | Nodes repel, edges act as springs | General networks, organic feel |
| ForceAtlas2 | GPU-accelerated, smooth clustering | Large dense graphs (10-10k nodes) |
| Kamada-Kawai | Preserves graph-theoretic distance | Relationship distances |
| Fruchterman-Reingold | Attractive + repulsive with cooling | Aesthetic drawing |
Hierarchical/Layered
| Algorithm | Description | Best For |
|---|---|---|
| Sugiyama | Nodes in layers, minimize edge crossings | Directed flows, org charts |
| Reingold-Tilford | Tree-specific, top-down | Pure hierarchies |
| Radial/Twopi | Concentric circles from root | Dependency chains |
Space-Filling
| Algorithm | Description | Best For |
|---|---|---|
| Treemap | Nested rectangles, area = weight | Proportions in hierarchy |
| Voronoi | Each node claims surrounding space | Territory/zone boundaries |
| Circle Packing | Nested circles containing children | Hierarchical containment |
Recommended Approach
Hybrid strategy:
- Force-directed for positioning siblings (organic, handles unknown cardinality)
- Hierarchical constraints to keep children within parent bounds
- Semantic zoom to swap representations at threshold
- Animation choreography for sequenced transitions
Semantic Zoom Techniques
From ZUI research:
| Technique | Description |
|---|---|
| Level of Refinement (LOR) | Quick sketches at LOR 0, full detail at higher levels |
| Pixel-threshold culling | Objects < 1px hidden; tree traversal stops early |
| Structure transformation | Objects change shape/detail/presence, not just scale |
References
Relationship to Other Cycles
- c1.border-grouping: Zones may become composite actors; ForceClustering strategy is relevant
- c2.entity-meshes: Leaf actors use these meshes; composites need new container representation
- c3.data-flows: Flows must support aggregation/disaggregation based on zoom level
Progress
- Test harness: HTML page with zoom radio buttons
- Test data: Hierarchical structure (1 Company → 12 Teams → ~166 Apps)
- Basic zoom level rendering (cubes at each level)
- ContainerRug class: flat labeled planes for parent context
- Grid layout for actors within containers
- Nested rugs at Apps level (company + team rugs)
- Design composite actor data model
- Implement CompositeActor class extending BaseMesh
- Collapsed state rendering (cube + badge)
- Expand/collapse animation
- Layout agent prototype (force-directed with containment)
- Flow aggregation logic
- Semantic zoom thresholds
- Sibling muting on drill-down
Test Data Distribution
| Team | App Count |
|---|---|
| Platform | 18 |
| Payments | 12 |
| Identity | 8 |
| Search | 15 |
| Analytics | 22 |
| Mobile | 10 |
| Infrastructure | 24 |
| Data | 14 |
| Frontend | 9 |
| Backend | 16 |
| DevOps | 7 |
| Security | 11 |
Total: ~166 apps
Open Questions
- What triggers zoom level change? Camera distance? User click? Both?
- Should expansion be animated or instant with fade?
- How deep can nesting go before UX degrades?
- How do zones (c1) and composite actors interact?
Lessons Learned
Scene Object Cleanup (Three.js + Vue)
Problem: Objects weren't being removed when switching zoom levels, even though tracking arrays were cleared.
Root cause: Relying on tracking arrays (renderedCubes, renderedRugs) to stay in sync with the actual Three.js scene is fragile. The removeObject helper from ExperimentBase wasn't reliably removing objects.
Solution: Clear directly from the scene, not from tracking arrays:
function clearScene() {
const scene = sceneManager.value?.getScene();
if (!scene) return;
// Copy children array before modifying
const toRemove = [...scene.children].filter(obj => {
// Keep lights, cameras, grid helpers
if (obj.isLight || obj.isCamera || obj.isGridHelper) return false;
return obj.isMesh || obj.isGroup;
});
toRemove.forEach(obj => {
scene.remove(obj);
// Dispose recursively...
});
}
Key patterns:
- Copy
scene.childrenbefore iterating ([...scene.children]) - Use Three.js type checks:
obj.isMesh,obj.isGroup,obj.isLight,obj.isGridHelper - Dispose geometry and materials recursively via
obj.traverse() - Don't trust wrapper functions—go straight to the scene when debugging
Visual record
5 captures — heroes, page slices, and full-page renders. Click to view.




