Live Wall

Live Wall: Frontend Architecture

This page covers the frontend architecture of the Live Connection Wall, including the graph library, data model, state management patterns, and all the configuration constants you can tweak.


Graph Library

The wall uses the react-force-graph library, which wraps D3's force simulation:

  • 2D: react-force-graph-2d, which renders on an HTML Canvas
  • 3D: react-force-graph-3d, which renders using Three.js / WebGL

Both use a force-directed layout where:

  • Nodes repel each other (like magnets)
  • Links pull connected nodes together (like springs)
  • The simulation finds a balanced layout automatically

Data Model

The graph stores two things: nodes (people) and links (connections).

// A person in the graph
interface WallNode {
  id: string;        // Profile UUID
  name: string;      // e.g. "Jay Park"
  avatar?: string;   // Profile picture URL
  major?: string;    // e.g. "Computer Science"
  year?: number;     // e.g. 4
  x?: number;        // Current x position (set by physics)
  y?: number;        // Current y position (set by physics)
  __born?: number;   // Timestamp when added (for intro animation)
}

// A connection between two people
interface WallLink {
  source: string;    // Node ID
  target: string;    // Node ID
  createdAt: number; // When the connection was made
  __born?: number;   // Timestamp when added to graph
}

State Management

The wall uses a ref-based architecture instead of putting everything in React state. This is intentional because the force graph library mutates node positions every frame, and re-rendering React on every physics tick would be way too slow.

// These are refs, NOT state - they don't cause re-renders
const graphDataRef = useRef({ nodes: [], links: [] });
const nodesByIdRef = useRef({});        // Quick lookup: id → node
const neighborsRef = useRef(new Map()); // id → Set of neighbor ids
const pairKeySetRef = useRef(new Set()); // Prevents duplicate links

The component uses a dataTick state counter that gets incremented when the graph data changes. This triggers derived computations (like degree calculations) without re-rendering the entire graph:

const [dataTick, setDataTick] = useState(0);
// Increment when data changes:
setDataTick(t => t + 1);

Refs vs State

If data needs to be read every animation frame (like node positions), use a ref. If data drives UI updates (like the ticker list), use state. This is the most important architectural decision in the wall.


Tunables

At the top of ConnectionWall.tsx, you'll find a block of constants that control everything about the visualization:

ConstantDefaultWhat it controls
VIS1Global scale factor for all sizes
CHARGE_BASE-80How strongly nodes repel each other
CHARGE_PER_DEG-8Extra repulsion per connection
COLLIDE_BASE18Minimum distance between nodes
INTRO_MS800How long the "pop in" animation takes
SPOTLIGHT_MS6000How long the spotlight banner shows
HALO_RECENT_MS10000How long the "just connected" glow lasts
AUTOPAN_INTERVAL_MS6000How often the camera moves to a new node
TRAIL_WINDOW_MS8000How long trail lines persist
HEATMAP_WINDOW_MS30000Time window for heatmap glow
TICKER_MAX50Maximum items in the scrolling ticker

Tip for customization

If the graph looks too spread out or too cramped, adjust CHARGE_BASE (more negative = more spread). If nodes overlap, increase COLLIDE_BASE.

Previous
Overview