Teleport VFX System (February 2026)
Commits: 7bf0e14, ceb8909, 061e631PR: #939
Author: dreaminglucid
Overview
Complete rewrite of the teleport visual effects system with object pooling, multi-phase animation, and TSL shader materials. Replaces the simple beam/ring/particles effect with a spectacular multi-component sequence featuring ground rune circles, dual beams with elastic overshoot, shockwave rings, helix spiral particles, burst particles with gravity, and dynamic point lighting.Visual Design
Phase Timeline
The effect runs for 2.5 seconds with 4 distinct phases:| Phase | Duration | Progress | Description |
|---|---|---|---|
| Gather | 0.0s - 0.5s | 0% - 20% | Rune circle appears and scales, base glow fades in |
| Erupt | 0.5s - 0.85s | 20% - 34% | Beams shoot upward with elastic overshoot, core flash, shockwaves |
| Sustain | 0.85s - 1.7s | 34% - 68% | Full effect sustained, helix particles spiral, burst particles launched |
| Fade | 1.7s - 2.5s | 68% - 100% | All components fade out, beams thin and shrink |
Components
Structural Elements (7 meshes per pool entry):- Ground Rune Circle - Procedural canvas texture with concentric circles, radial spokes, and triangular glyphs
- Base Glow Disc - Pulsing cyan glow at ground level
- Inner Beam - White→cyan gradient cylinder with elastic height curve
- Outer Beam - Light cyan→dark blue cylinder, delayed 0.03s
- Core Flash - White sphere that pops at eruption (0.20-0.22s)
- Shockwave Ring 1 - Expanding ring with easeOutExpo (scale 1→13)
- Shockwave Ring 2 - Second ring delayed 0.024s (scale 1→11)
- Helix Particles (8): 2 strands of 4, spiral upward with decreasing radius
- Burst Particles (6): 3 white + 3 cyan, launched with gravity simulation
- Point Light: Dynamic intensity (0→5.0 at eruption, fades to 0)
Technical Implementation
Object Pooling
Pool Size: 2 concurrent effects (both duel agents can teleport simultaneously) Zero Allocations: All materials compiled duringinit(), no pipeline compilations at spawn time
Pool Entry Structure:
Shared Resources
Geometries (allocated once, shared by all pool entries):particleGeo: PlaneGeometry(1, 1)beamInnerGeo: CylinderGeometry with bottom pivotbeamOuterGeo: CylinderGeometry with bottom pivotdiscGeo: CircleGeometry(0.5, 16)runeCircleGeo: CircleGeometry(1.5, 32)shockwaveGeo: RingGeometry(0.15, 0.4, 24)sphereGeo: SphereGeometry(0.4, 8, 6)
runeTexture: CanvasTexture with procedural rune pattern
particleCyanMat: Cyan glow for helix particlesparticleWhiteMat: White glow for burst particles
TSL Shader Materials
Particle Glow Material (no per-instance opacity):Animation Curves
Beam Elastic Curve (Hermite interpolation):Easing Functions
- Gather Phase: easeOutQuad (smooth fade-in)
- Fade Phase: easeInQuad (smooth fade-out)
- Shockwaves: easeOutExpo (fast expansion, slow deceleration)
Particle Behavior
Helix Spiral Particles
Count: 8 (2 strands of 4 particles each) Motion:baseScale * heightFade
Burst Particles
Count: 6 (3 white + 3 cyan) Motion:baseScale * (1.0 - localTime / 1.8)
Culling: Hidden when below ground (y < -0.5)
Event Handling
Suppressing Effects
Teleport effects can be suppressed viasuppressEffect flag:
- Mid-fight proximity corrections (duel system)
- Frequent position adjustments
- Invisible teleports
Network Propagation
ThesuppressEffect flag is forwarded through the network stack:
Duplicate Effect Prevention
Fixed: Removed duplicatePLAYER_TELEPORTED emits that caused ghost effects:
- PlayerRemote.modify(): Removed emit (position may be stale)
- ClientNetwork.onPlayerTeleport(): Only emits for remote players (local player already emitted in
localPlayer.teleport())
Performance Characteristics
Spawn Cost
- Before: ~20 material compilations, ~20 geometry allocations
- After: 0 allocations (grab from pool, reset state)
Update Cost
- Before: ~20 material opacity updates, ~20 particle position updates
- After: 7 uniform updates, 14 particle position updates (phase-gated)
Memory
- Before: ~20 materials × 2 concurrent effects = 40 materials
- After: 7 materials × 2 pool entries + 2 shared particle materials = 16 materials
Draw Calls
- Before: ~20 draw calls per effect
- After: ~20 draw calls per effect (same, but zero allocation overhead)
Debugging
Disable Cache for Testing
Visual Debugging
Performance Profiling
Related Systems
ClientTeleportEffectsSystem
File:packages/shared/src/systems/client/ClientTeleportEffectsSystem.ts
Responsibilities:
- Listen for
PLAYER_TELEPORTEDevents - Spawn effects from object pool
- Update all active effects each frame
- Deactivate effects when complete
DuelOrchestrator
File:packages/server/src/systems/StreamingDuelScheduler/managers/DuelOrchestrator.ts
Changes:
- Removed
suppressEffect: truefrom cleanup teleports (exit VFX now plays) - Victory emote delayed 600ms to prevent combat cleanup override
- Emote reset to “idle” in
stopCombat()so wave stops when agents teleport out
ServerNetwork
File:packages/shared/src/systems/client/ClientNetwork.ts
Changes:
- Forward
suppressEffectthrough network packets - Removed duplicate
PLAYER_TELEPORTEDemits
Migration Guide
For Developers
No migration needed - changes are fully backward compatible. Triggering teleport effects:For Asset Creators
Rune Circle Texture: Procedurally generated via canvas - no external assets needed. Colors: Hardcoded cyan/white theme - modify increateRuneTexture() and material factories if needed.
Known Issues
Beam Base Clipping
Symptom: Beam base clips through floor on uneven terrain. Cause: Beam geometry starts at y=0, floor may be below. Fix (commit ceb8909): Fade beam base to prevent VFX clipping through floor:Duplicate Teleport VFX
Symptom: 3 teleport effects play when agents exit arena (should be 2). Cause: Race condition -clearDuelFlagsForCycle() called before cleanupAfterDuel() teleports, causing DuelSystem.ejectNonDuelingPlayersFromCombatArenas() to emit spurious extra teleport.
Fix (commit 7bf0e14): Defer flag clear until cleanup teleports complete:
References
- ClientTeleportEffectsSystem.ts - Implementation
- DuelOrchestrator.ts - Duel system integration
- Three.js Shading Language - TSL documentation