- MeshBuilder API: https://doc.babylonjs.com/features/featuresDeepDive/Meshes/Mesh/CreateMesh_Details
Resources
28Install
npx skillscat add mmongan/mmongan-github-io Install via the SkillsCat registry.
ChartXR: 3D Dependency Visualization Skill
Skill Overview
This specialized skill handles the complete implementation of dependency tree analysis, visualization generation, and interactive 3D scene management for ChartXR. Use this skill when building features that involve analyzing project structure, creating 3D representations, or enhancing scene interaction.
When to Use This Skill
- Adding new visualization elements (spheres, lines, annotations)
- Implementing complex camera behaviors or navigation
- Debugging dependency tree generation or parsing issues
- Optimizing scene rendering performance
- Creating interactive drilling/zoom features into nested structures
- Handling WebGL errors or graphics state issues
Core Knowledge Areas
Dependency Tree Architecture
The system analyzes source code and creates a hierarchical tree:
Parsing Phase (
buildDependencyTree.js)- Extracts imports:
import X from 'path',require('path') - Extracts exports:
export const/function/class NAME,export { NAME } - Detects cycles with visited/inProgress Sets
- Limits depth to 20 levels to prevent runaway recursion
- Extracts imports:
Data Structure (NodeInfo interface)
interface NodeInfo { name: string; // "package.json" imageUrl: string; // Profile image children: NodeInfo[]; // Dependencies exports: string[]; // Exported symbols position: Vector3; // 3D world position size: number; // Cube dimension nodeMesh: Mesh; // Babylon.js geometry }Visualization Mapping
- One cube per file node
- Cyan spheres = imported dependencies (children)
- Gold spheres = exported objects (exports[])
- Blue tubes = dependency connections with red arrow heads
3D Scene Mathematics
Position Calculation
- Root node: (0, -10, 0)
- Child nodes arranged in circular orbit around parent
- Angle per child:
(i / numChildren) * 2π - Height alternates:
±nodeSize * 0.15 - Radius:
nodeSize * 0.25(adjustable parameter)
Surface-Based Connections
// Direction from source to destination
const direction = toNode.position.subtract(fromNode.position);
const normalizedDir = direction.normalize();
// Surface points (not center-to-center)
const fromSurfaceOffset = (fromSize / 2) * 1.05; // 1.05 = slight overshoot
const toSurfaceOffset = (toSize / 2) * 1.05;
const startPoint = fromNode.position.add(normalizedDir.scale(fromSurfaceOffset));
const endPoint = toNode.position.add(normalizedDir.scale(-toSurfaceOffset));
// Create tube between these points
MeshBuilder.CreateTube(..., { path: [startPoint, endPoint], ... })Arrow Head Orientation
// Rotate cone to point along direction vector
const yAxis = Vector3.Up();
const rotationAxis = Vector3.Cross(yAxis, normalizedDir).normalize();
const rotationAngle = Math.acos(Vector3.Dot(yAxis, normalizedDir));
if (rotationAxis.length() > 0.001) {
const quat = Quaternion.RotationAxis(rotationAxis, rotationAngle);
arrow.rotationQuaternion = quat;
}Texture Generation
- DynamicTexture at 256x256 for file name labels
- Canvas-based text rendering with fallback handling
- Single texture applied to all 6 cube faces (shared material)
- Try-catch wraps creation to handle WebGL context issues
const faceTex = new DynamicTexture(`face_${name}`, 256, scene, false);
const ctx = faceTex.getContext();
ctx.fillStyle = '#4A90E2';
ctx.fillRect(0, 0, 256, 256);
ctx.font = 'bold 32px Arial';
ctx.fillStyle = '#FFFFFF';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(displayText, 128, 128);
faceTex.update();Camera System
- Type: FreeCamera with minZ = 0.5 (near clipping plane)
- Collision: Automatic detection prevents camera clipping into geometry
- Landing: Face-center mode to zoom into specific cube surfaces
- Movement: WASD keys + mouse look (via inputController)
Collision Detection Logic
// Check nearby nodes within search radius
const nearby = nodeManager.getNearbyNodes(cameraPosition, 200);
// If colliding, back away smoothly
if (collision) {
camera.position = previousSafePosition;
}Material System
All materials follow a consistent pattern:
- diffuseColor: Base color visible in light
- emissiveColor: Self-luminosity for glow effect (usually darker than diffuse)
- specularColor: Highlight response (typically 0.5, 0.5, 0.5)
- specularPower: Shininess (32 for glossy)
- alpha: Transparency (0.6-0.9 depending on element type)
Implementation Patterns
Adding a New Mesh Type
- Create MeshBuilder geometry:
CreateSphere(),CreateBox(),CreateCone(), etc. - Create StandardMaterial with appropriate colors
- Parent to correct TransformNode
- Set pickable/visible properties
- Add to scene via parent hierarchy
Debugging 3D Issues
- Use babylon.js debug layer:
scene.debugLayer.show() - Check WebGL errors: browser DevTools → Console → filter WebGL
- Verify positions: log Vector3 coordinates during creation
- Test camera: manually adjust minZ value to isolate clipping issues
- Profile rendering: DevTools Performance tab during interaction
Performance Optimization
- Limit mesh count per node (8 refs + 16 exports = 24 max per file)
- Use TransformNode hierarchies for grouped movement
- Single texture per node instead of per-face
- Freeze meshes after creation if not animating
Common Tasks
Task: Extract Exports from New File Type
// Pattern to add to extractExports() function
const newExportRegex = /pattern_for_declaration/g;
while ((match = newExportRegex.exec(content)) !== null) {
exports.push(match[1]);
}Task: Add Interactive Feature
- Bind event in pointerHandler.ts or inputController.ts
- Get intersected mesh:
scene.pick(x, y) - Identify node: look up in
nodeInfoMapby mesh name - Execute action (zoom, highlight, navigate, etc.)
- Update scene state if needed
Task: Optimize Large Dependency Trees
- Implement LOD (level of detail) - hide distant node details
- Use bounding box culling to skip invisible meshes
- Profile to identify bottleneck (parsing vs rendering vs interaction)
- Consider async mesh creation for complex trees
Known Limitations & Workarounds
WebGL Context Loss
Issue: Graphics context can be lost on some devices/browsers
Workaround: Try-catch around texture creation with fallback to solid color
Prevention: Minimize memory usage; avoid creating too many large textures
Large Dependency Trees
Issue: Performance degrades with very large projects (1000+ files)
Workaround: Implement LOD or culling
Future: Consider GPU-accelerated rendering or WebWorker for parsing
Arrow Head Rotation
Issue: Cone must point along arbitrary direction vector
Solution: Use quaternion rotation with cross product to find axis
Edge case: Collinear vectors (direction = -Y axis) require special handling
Testing Checklist
- Scene initializes without WebGL errors
- All node meshes render with correct colors and transparency
- Connection tubes connect cube surfaces (not centers)
- Arrow heads point toward destination
- Export spheres visible and distinct from reference spheres
- Texture labels readable on all faces
- Camera collision works (can't clip into cubes)
- Performance acceptable (60 FPS on target hardware)
Resources
- Babylon.js Documentation: https://doc.babylonjs.com/
- Vector3 Math: https://doc.babylonjs.com/features/featuresDeepDive/Meshes/geometry/reference_frame#vectors
- Quaternion Rotation: https://doc.babylonjs.com/features/featuresDeepDive/Quaternions
- MeshBuilder API: https://doc.babylonjs.com/features/featuresDeepDive/Meshes/Mesh/CreateMesh_Details