Sigma.js

repository·main·Indexed 11 days ago

https://github.com/jacomyal/sigma.js

An open-source JavaScript library for visualizing large-scale graphs with thousands of nodes and edges using WebGL. Built on top of the graphology library, it supports advanced rendering features through plugins including curved edges (@sigma/edge-curve), image nodes (@sigma/node-image), image export (@sigma/export-image), and background map layers for Leaflet (@sigma/layer-leaflet) and Maplibre (@sigma/layer-maplibre).

Tokens
40.8K
Snippets
137
Records
206
Agent score
93%

What's inside Sigma.js

  1. Introduction to Sigma.js

    main

    Sigma.js is a high-performance library for visualizing large graphs and networks. It is designed to handle complex data structures and provides various ways to render nodes, edges, and layers (such as WebGL, Leaflet, or MapLibre) to create interactive and visually rich graph visualizations.

    To use Sigma.js in your project, you can install the core package and various specialized plugins for specific rendering needs (e.g., @sigma/layer-webgl for performance, @sigma/layer-leaflet for geographic data, or @sigma/node-image for custom node visuals).

    # Example installation of the core package
    npm install sigma
  2. How rendering works in sigma.js

    main

    Sigma uses a two-step rendering mechanism to visualize data:

    1. Processing: Sigma processes the graph data. This includes invoking nodeReducer and edgeReducer settings and indexing data for WebGL renderers.
    2. Rendering: Sigma generates the visual layers within the canvas element.

    Automatic Rendering Triggers

    Sigma automatically triggers processing and rendering when:

    • Graphology Events: The underlying graph emits data update events.
    • Settings Updates: Any setting is modified.
    • User Interactions: Mouse or touch interactions update the camera.

    Manual Rendering Triggers

    If an external factor changes a state used by your nodeReducer or edgeReducer (without triggering a Graphology event), you must manually trigger a refresh. Use the following methods:

    • Sigma#refresh: Immediately processes the data and then renders it. This can be resource-intensive.
    • Sigma#scheduleRefresh: Schedules a refresh for the next animation frame using requestAnimationFrame. It is debounced; if a refresh is already scheduled, it won't schedule another.
    • Sigma#scheduleRender: Schedules a render for the next frame. Use this if you only need to re-render without re-processing data. It will not schedule a new render if one is already pending.

    Note: Do not attempt to call the private render method directly; use scheduleRender instead.

  3. Understand Sigma.js rendering layers

    main

    Sigma.js renders graphs using multiple stacked layers to optimize performance and interaction. Some layers use WebGL for high-performance rendering (edges, nodes), while others use Canvas for elements like labels or hover effects.

    By default, all layers are placed within the sigma container with position: absolute; and inset: 0;.

  4. Use Instanced Rendering for performance

    main

    To avoid redundant data transmission to the GPU, Sigma v3 supports instanced rendering. This is highly recommended for any program using WebGLRenderingContext.TRIANGLES (like circles or rectangles).

    To implement instanced rendering, your program's getDefinition method must provide:

    • CONSTANT_ATTRIBUTES: An array of attributes related to each vertex (similar to the standard ATTRIBUTES array).
    • CONSTANT_DATA: An array of data for each vertex.

    This allows the GPU to use one buffer for item-specific data and another for vertex-specific data.

  5. How node image rendering works with textures atlas

    main

    Images in @sigma/node-image are stored in a textures atlas.

    Key architectural details:

    • Atlas Lifecycle: The atlas is bound to the class, not the instance. This means the atlas is preserved even if the Sigma instance is respawned.
    • Multiple Textures: Because of the GL_MAX_TEXTURE_SIZE limit, the atlas can utilize multiple textures simultaneously.
    • Factory Pattern: Because the atlas is bound to the class, the main export is createNodeImageProgram. This factory creates a renderer class that can be bound to a specific atlas, allowing for different renderers to coexist.
  6. Implement picking in custom programs

    main

    To enable collision detection (picking) between a user interaction (like a mouse click) and nodes/edges, your program must support rendering a hidden 'picking image'. In this image, each item is drawn with a unique color.

    To implement this, use the PICKING_MODE preprocessor in your shaders:

    • When PICKING_MODE is false, the shader renders the normal visual output (colors, antialiasing, etc.).
    • When PICKING_MODE is true, the shader renders the unique color used for identification.

    By checking the color of the pixel at the interaction point in the picking image, Sigma.js can identify which item was clicked.

  7. How custom NodeProgram and EdgeProgram work

    main

    Sigma.js uses WebGL for rendering. To create a custom renderer, you must implement a class that extends NodeProgram or EdgeProgram. These classes manage the lifecycle and data bindings for WebGL.

    A custom program requires:

    1. Vertex and Fragment Shaders: Essential for processing graphical data (positioning and coloring).
    2. Program Definition: Describes the number of vertices per item, and the attributes (per-vertex) and uniforms (constant per item) passed to the shaders.
    3. The Program Class Implementation:
      • getDefinition(): Returns the program definition.
      • processVisibleItem(offset: number, data: NodeDisplayData): Populates the internal this.array with values for visible items.
      • draw(params: RenderParams): Manages uniform values and executes the gl.drawArrays call.

    Sigma.js also provides helpers to compose programs for easier development.

  8. How the Sigma.js event handling API works

    main

    Sigma.js uses an event-driven architecture modeled after the Node.js events package. This allows you to execute code in response to user interactions or internal lifecycle changes.

    While the API follows the standard EventEmitter pattern, events and their payloads are typed, providing better developer experience and type safety, particularly when using TypeScript.

  9. Understand the viewport space: `viewport`

    main

    The viewport space refers to the 2D canvas coordinates, typically measured in pixels using width and height. This space is essential when drawing labels or handling user mouse events.

    Key characteristics:

    • Flipped Y-axis: Unlike the graph space, the y dimension is flipped; higher y values represent positions lower on the screen.
    • Aspect Ratio Correction: Sigma automatically corrects for the viewport's aspect ratio (and any optional padding) to ensure the graph occupies the maximum available screen space without distortion.
  10. Understand the core lifecycle of a sigma instance

    main

    A sigma instance follows a lifecycle consisting of instantiation, settings management, and termination.

    Instantiation

    To initialize a sigma instance, you must provide:

    • A Graphology Instance: The essential graph data structure sigma visualizes.
    • A DOM Element: The container for the visualization.
    • Settings (Optional): Initial configuration for sigma's behavior.

    You can update the graph after instantiation using the setGraph method.

    Settings Management

    Settings control sigma's behavior and can be managed in two ways:

    1. At instantiation: Passed directly to the constructor.
    2. Post-instantiation: Modified using setSetting or updateSetting methods.

    Termination

    To prevent memory leaks and ensure efficient garbage collection, always call the kill method to gracefully terminate a sigma instance and release all internal bindings and resources.

    // Example conceptual flow
    const sigmaInstance = new Sigma(graph, container, settings);
    
    // ... later ...
    
    sigmaInstance.setGraph(newGraph);
    sigmaInstance.setSetting('someSetting', value);
    
    // ... when finished ...
    
    sigmaInstance.kill();
  11. Implement Picking in WebGL programs

    main

    Sigma v3 uses a GPU-based picking mechanism. It renders two additional layers where each node/edge is drawn with a unique color representing its ID. To implement this in a custom program:

    1. Data Transfer: In processVisibleItem, the ID is provided as a 4-byte encoded value. This must be passed to the shader.
    2. Vertex Shader: Use the PICKING_MODE macro to select between the standard color and the ID color (as shown in the code example).
    3. Fragment Shader: In picking mode, pixels must be either uncolored or colored exactly with the ID. Do not use antialiasing, as it can bleed colors and result in incorrect ID detection.