Three.js 3D visualization system with interactive reactor scene, separate C Signet viewport with magnetic interaction, and stage-based animation management.
Reactome Visualization - Interactive 3D WebGL scene displaying procedural geometry (rings & cylinders) with orbit camera controls and post-processing effects.
CSignetApp - Isolated 320×320px viewport for the "C" signet with magnetic pull interaction (drag constrained to 0.03 units, smooth 800ms return with wobble).
Configuration-Driven - Material properties, extrusion settings, and positioning cascade through config objects (CONNECTOME_LOGO_CONFIG, C_SIGNET_CONFIG) with fallback defaults.
Stage Manager - Manages scene transitions with easing animations between defined states (initial, focus, reveal, etc.).
js/
├── App.js
│ ├── ReactomeApp # Main visualization (reactor, lights, controls, effects)
│ ├── CSignetApp # Standalone C Signet with interaction & fade
│ ├── DEFAULT_CONFIG # UI sync configuration
│ └── exports: ReactomeApp, CSignetApp, DEFAULT_CONFIG
├── main.js # Entry point, UI event bindings, initialization
├── effects/
│ └── SciFiShader.js # Post-processing (bloom, scanlines, glitch)
├── geometries/
│ ├── ConnectomeLogo.js # createConnectomeLogoGeometry(), createCSignetGeometry(), material variations
│ ├── DottedCylinder.js # createDottedCylinderGeometry()
│ └── Ring.js # createRingGeometry()
└── utils/
├── random.js # randomFloat(), randomInt(), randomPick()
└── StageManager.js # Stage definitions, easing functions
index.html # Fixed structure: header, collapsible sidebar, canvas container
styles.css # Fixed styling, responsive breakpoints
constructor(options) // container, backgroundColor, cameraDistance, objectCount, etc.
start() // Begin animation loop
stop() // Stop animation
setCameraDistance(d) // Orbit camera zoom (triggers cSignet fade check)
getCameraDistance() // Returns distance from origin
setOpacity(val) // Material opacity
setRotationSpeed(val) // Animation speed multiplier
setShaderEnabled(bool) // Post-processing toggle
constructor(options) // container, size (default 320)
loadCSignet(options) // Async: Load geometry with config
loadCSignet({
scale, xOffset, yOffset, zOffset,
color, opacity, transparent, wireframe,
roughness, metalness,
depth, bevelEnabled, bevelThickness, bevelSize, bevelSegments
})
start() // Begin animation & rendering
setVisibilityByDistance(bool) // Called from ReactomeApp._tick() when distance < 1
setCSignetVisible(bool) // Toggle visibility
setCSignetScale(s) // Transform scale
setCSignetPos{X,Y,Z}(val) // Transform position
setCSignetRot{X,Y,Z}(rad) // Transform rotation
DEFAULT_CONFIG provides initial valuesDEFAULT_CONFIG valuessetCameraDistance() read merged configmain.js creates event listeners that update config & call app methodsLOGO_VARIATIONS {
SOLID: // MeshStandardMaterial with wireframe property
WIREFRAME: // MeshBasicMaterial with wireframe=true
GLASS: // MeshPhysicalMaterial (transparent, physical)
HOLOGRAM: // Dual materials (solid + wireframe) with config.opacity
NEON: // MeshStandardMaterial with emissive
}
Config-to-Material Pipeline:
createConnectomeLogoGeometry({opacity, color, wireframe, ...})
→ createExtrudedSVG() with merged config
→ createMaterial(variation, config)
→ Material respects all config properties
Default fallback: CONNECTOME_LOGO_CONFIG.* ?? C_SIGNET_CONFIG.*
State Flow:
cSignetTargetOpacity = 0 (invisible)_tick() checks getCameraDistance()cSignetApp.setVisibilityByDistance(true) → cSignetTargetOpacity = cSignetNormalOpacity_tick() interpolates opacity over cSignetFadeDuration (300ms)setVisibilityByDistance(false) → fades to 0Smooth interpolation in CSignetApp._tick():
const fadeSpeed = 1 / this.cSignetFadeDuration;
diff = target - current;
if (Math.abs(diff) > 0.001) {
opacity += diff * (delta * 1000 * fadeSpeed);
}
Constraints:
0.03 units (very restricted)800ms with ease-out cubic12Hz oscillation, dampens to 0 during returnEvent Flow:
mousedown / touchstart → raycasting on hitbox → _onInteractionStart()_onInteractionMove() → position clamped to maxDragDistancemouseup / touchend → _onInteractionEnd() → enable isReturningWobble Math:
wobbleAmount = (1 - easeProgress) * 0.15; // Starts 15%, fades to 0
wobbleFactor = sin(progress * 12 * π * 2); // 12 Hz oscillations
finalOffset = easeOffset + (wobbleFactor * wobbleAmount)
Slider binding (main.js):
setupRangeControl(
'slider-id',
'display-id',
(value) => app.setMethod(value),
(v) => v.toFixed(2)
);
Toggle binding (main.js):
const toggle = document.getElementById('toggle-id');
toggle.addEventListener('change', (e) => {
app.setProperty(e.target.checked);
});
Config update sequence:
app.setXxx(value)setXxx() updates this.property and DOM rendering_tick() updateAdd new parameter:
DEFAULT_CONFIG in App.jssetNewParam(val)main.js: setupRangeControl() or event listener_tick() or update methodAdd new geometry:
geometries/NewGeom.js_createObjects() or as neededcreateMaterial() pipelineModify material:
createMaterial() in ConnectomeLogo.jscreateConnectomeLogoGeometry() or loadCSignet()cd /home/qlb/projects/skills/reactome
python3 -m http.server 8000
# Open http://localhost:8000
No build step required - ES modules via import maps, Three.js from CDN.
|diff| < 0.001 (tolerance)_tick())