Zum Seitenanfang

BLOG-POST MODULE

NebulaGraph: 1 Mio. Datenpunkte bei 60 fps mit WebGL

  • Veröffentlicht: 19.2.2026
  • 8 Min. Lesezeit
  • Aufrufe: 767

NebulaGraph: Rendern einer Million Datenpunkte in Echtzeit mit WebGL

Versuchen Sie, eine Million Datenpunkte in einer Tabellenkalkulation oder sogar auf herkömmliche Weise zu visualisieren Diagrammbibliothek. Sie erhalten eines von zwei Ergebnissen:

  1. Es stürzt Ihren Browser ab (50+ MB DOM-Knoten)
  2. Es wird in 30 Sekunden gerendert (Benutzer gibt auf, bevor er die Visualisierung sieht)

Datenwissenschaftler arbeiten oft mit so großen Datensätzen – Aktienkurskorrelationen, Genexpressionsmatrizen, Einbettungen neuronaler Netzwerke. Aber vorhandene Visualisierung Die Werkzeuge erreichten etwa 100.000 Punkte, bevor sie erstickten.

Dies ist die Geschichte von NebulaGraph, einer WebGL-basierten Visualisierungs-Engine, die wir haben Entwickelt, um riesige Datensätze mit Echtzeit-Interaktivität zu verarbeiten.

Das Problem: Das DOM kann nicht skaliert werden

Ein traditioneller Ansatz zur Datenvisualisierung:

// Plotting 1 million points the naive way
const svg = d3.select("body").append("svg");
data.forEach((point) => {
  svg.append("circle")
    .attr("cx", point.x)
    .attr("cy", point.y)
    .attr("r", 2)
    .style("fill", colorScale(point.value));
});

Was geschieht?

  • Erstellen Sie 1.000.000 DOM-Knoten
  • Die CSS-Engine berechnet das Layout für jeden (1 Mio. Layoutberechnungen).
  • Browserspeicher: 50-100 MB nur für die Kreise
  • Interaktion (Zoom, Schwenken, Schweben): Für mehr als 2 Sekunden einfrieren

Der Flaschenhals: Das DOM wurde für Dokumente entwickelt, nicht für Millionen von Dokumenten grafische Objekte. Jedes Element ist ein Knoten mit vollem Funktionsumfang, der ein Ereignis haben kann Zuhörer, Stile, Animationen – umfangreiche Infrastruktur für etwas, das nur ein ist Pixel.

Datenwissenschaftler würden:

  • Verwenden Sie Python mit Matplotlib (keine Interaktivität, statisches PNG)
  • Verwenden Sie Plotly (interaktiv, aber langsam > 50.000 Punkte)
  • Verwenden Sie spezielle Tools (Graphia für die grafische Darstellung, Tableau für Geschäftsinformationen).
  • Erstellen Sie benutzerdefiniertes WebGL (erfordert Grafikkenntnisse, die die meisten nicht haben)

Wir fragten: Könnten wir WebGL für Datenwissenschaftler ohne Grafiken zugänglich machen? Sachverstand?

Die Lösung: GPU-beschleunigtes Rendering

Wir haben NebulaGraph in drei Schichten erstellt:

Schicht 1: WebGL-Renderer mit GLSL-Shadern

Anstelle von DOM-Elementen ist jeder Punkt in WebGL ein einzelnes Dreieck, gerendert von die GPU:

// vertex.glsl - Runs once per vertex (1M times, in parallel on GPU)
attribute vec3 position;
attribute float value;  // Data value for coloring
attribute float selected; // For highlighting

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform float time;

varying vec4 vColor;
varying float vValue;

void main() {
  // Position: transform to screen space
  gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0);
  
  // Size: responsive to zoom level
  gl_PointSize = mix(2.0, 8.0, float(selected));
  
  // Color: based on data value
  vColor = colorForValue(value);
  vValue = value;
}
// fragment.glsl - Runs once per pixel
varying vec4 vColor;
varying float vValue;

void main() {
  // Circular point shape (instead of square)
  float r = distance(gl_PointCoord, vec2(0.5));
  if (r > 0.5) discard; // Transparent outside circle
  
  gl_FragColor = vColor;
  gl_FragColor.a = 1.0 - smoothstep(0.4, 0.5, r); // Soft edges
}

Ergebnis: 1 Million Punkte gerendert in ~16 ms (60 fps) statt 30 Sekunden.

Schicht 2: Datenübertragungsoptimierung

Der Engpass verlagerte sich vom Rendering zur Datenübertragung:

// Traditional: Transfer JSON
const data = [
  { x: 0.1, y: 0.2, value: 0.5 },
  { x: 0.15, y: 0.25, value: 0.55 },
  // ... 999,998 more objects
  // Result: ~40MB of JSON
];

// NebulaGraph: Binary streaming
const floatBuffer = new Float32Array(1000000 * 3); // 12MB
let offset = 0;
data.forEach((point) => {
  floatBuffer[offset++] = point.x;
  floatBuffer[offset++] = point.y;
  floatBuffer[offset++] = point.value;
});

// Transfer via ArrayBuffer (native binary, 3x smaller than JSON)
const arrayBuffer = floatBuffer.buffer;

Durch die Verwendung von binären Float32Arrays anstelle von JSON haben wir die Übertragungsgröße reduziert 40 MB bis 12 MB (70 % Reduzierung). Für netzwerklastige Szenarien bedeutete dies 3-4 zweite Ladezeit statt 30+ Sekunden.

Schicht 3: Data Scientist-freundliche API

Wir haben die WebGL-Komplexität abstrahiert:

# Python data science workflow
import numpy as np
import pandas as pd
from nebulagraph import NebulaGraph

# Load dataset (e.g., gene expression data)
data = pd.read_csv("gene_expression.csv")
features = np.array(data[['gene1', 'gene2', 'gene3']].values)

# Project to 2D for visualization
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
points_2d = pca.fit_transform(features)

# Create interactive visualization
viz = NebulaGraph(
  data=points_2d,
  values=data['expression_level'],
  color_by='expression_level',
  title='Gene Expression Landscape'
)
viz.show()

Hinter den Kulissen, NebulaGraph:

  • Serialisierte das Numpy-Array in ein Binärformat
  • WebGL-Kontext in Jupyter geöffnet
  • Binärdaten zur GPU gestreamt
  • Mehr als 1 Mio. Punkte interaktiv gerendert

Ergebnis: Datenwissenschaftler haben kein WebGL geschrieben. Sie haben Python geschrieben.**

Die Ergebnisse: Demokratisierung der Datenvisualisierung

60fps bei 1 Million Punkten

  • Traditionelle Werkzeuge: Eingefroren bei 20–30 Punkten
  • Matplotlib: Statisch, keine Interaktivität
  • Handlung: 15–20 fps bei 50.000 Punkten, unbrauchbar bei 100.000+
  • NebulaGraph: 60 fps stabil bei 1 Mio. Punkten

Dies ermöglichte eine interaktive Erkundung: Zoomen, Filtern, Bewegen des Mauszeigers darüber einzelne Punkte, um Werte anzuzeigen.

80 % schnellere Ladezeiten

  • JSON-Übertragung (40 MB) + DOM-Rendering: 30–45 Sekunden
  • NebulaGraph-Binärübertragung + GPU-Rendering: 5–8 Sekunden
  • Für iterative Arbeitsabläufe (Laden, Erkunden, Parameter anpassen, neu laden): 20 Minuten Analyse → 5-Minuten-Analyse

Anwendungsfälle aus der Praxis

Finanzmodellierung (Aktienkorrelationen)

  • Visualisieren Sie mehr als 5.000 Aktien × mehr als 2.000 Handelstage (10 Mio. Punkte)
  • Identifizieren Sie Korrelationscluster sofort, indem Sie den Mauszeiger darüber bewegen
  • Zoomen Sie, um einzelne Aktienpfade innerhalb des Clusters zu vergleichen

Genexpressionsanalyse

  • Plotten Sie 20.000 Gene × 500 Proben (10 Mio. Punkte)
  • Farbe nach Zelltyp, sehen Sie, welche Gene koexprimiert werden
  • Bewegen Sie den Mauszeiger über Genmetadaten (Funktion, Signalweg, Krankheitsassoziation).

Visualisierung neuronaler Netzwerke

  • Zeichnen Sie 1 Mio. Neuronenaktivierungen aus dem trainierten Modell auf
  • Farbe nach Aktivierungsstärke
  • Identifizieren Sie wichtige Merkmalsdarstellungen

Tiefer Einblick in die technische Architektur

Speicherverwaltung

Der GPU-Speicher ist begrenzt (normalerweise 1–4 GB VRAM). Wir haben es aggressiv geschafft:

// Streaming data for massive datasets
class DataStreamBuffer {
  chunkSize = 100_000; // Process in chunks
  totalPoints: number;

  async *streamData(dataset: AsyncIterable<Point>) {
    let chunk = new Float32Array(this.chunkSize * 3);
    let offset = 0;

    for await (const point of dataset) {
      chunk[offset++] = point.x;
      chunk[offset++] = point.y;
      chunk[offset++] = point.value;

      if (offset === this.chunkSize * 3) {
        yield chunk; // Transfer chunk to GPU
        chunk = new Float32Array(this.chunkSize * 3);
        offset = 0;
      }
    }

    if (offset > 0) yield chunk.slice(0, offset); // Final partial chunk
  }
}

Dadurch können wir Datensätze, die größer als der GPU-Speicher sind, per Streaming visualisieren.

Interaktionsleistung

Beim Zoomen/Schwenken muss die Projektionsmatrix bei jedem Bild neu berechnet werden. Wir optimiert:

const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);

const animate = () => {
  // Only recalculate matrix if camera moved
  if (camera.hasChanged) {
    camera.updateProjectionMatrix();

    // Reuse projection matrix across all shaders
    shader.uniforms.projectionMatrix.value = camera.projectionMatrix;
    shader.uniforms.viewMatrix.value = camera.matrixWorldInverse;

    camera.hasChanged = false;
  }

  renderer.render(scene, camera);
  requestAnimationFrame(animate);
};

Ergebnis: Schwenken/Zoom blieb auch bei 1 Mio. Punkten bei 60 Bildern pro Sekunde.

Farbzuordnung im Maßstab

Effiziente Zuordnung von Millionen von Werten zu Farben:

// Pre-computed color lookup table (texture)
uniform sampler2D colorMap; // 256×1 texture with color gradient
uniform float minValue;
uniform float maxValue;

void main() {
  // Normalize value to [0, 1]
  float normalized = (vValue - minValue) / (maxValue - minValue);
  
  // Look up color from texture (very fast)
  vec4 color = texture2D(colorMap, vec2(normalized, 0.5));
  
  gl_FragColor = color;
}

Dadurch konnten aufwändige Berechnungen (Logarithmen, Smoothstep-Interpolationen) vermieden werden jedes Fragment.

Was wir anders machen würden

1. Begonnen mit den Grundlagen der Visualisierung

Wir haben zunächst eine funktionsreiche Benutzeroberfläche erstellt, bevor wir uns an das Kernrendering gemacht haben. Lektion: Perfekte Kernleistung zuerst, Funktionen zweitens.

2. Bessere Dokumentation für Datenformate

Datenwissenschaftler hatten Probleme mit der Frage: „Welches Format akzeptiert NebulaGraph?“ Wir sollten haben im Vorfeld weitere Vorlagen und Beispiele bereitgestellt.

3. Mobiler Support

Die ursprüngliche Version war nur für den Desktop verfügbar. Für mobiles WebGL gelten unterschiedliche Einschränkungen (kleineres Ansichtsfenster, begrenzter VRAM). Es hätte geholfen, das von Anfang an zu planen.

Wer braucht GPU-beschleunigte Visualisierung?

Die NebulaGraph-Architektur gilt für:

  • Data Science: Erforschung hochdimensionaler Datensätze (Clustering, Dimensionsreduktionsausgabe, Korrelationsmatrizen)
  • Netzwerkanalyse: Visualisierung von Diagrammstrukturen mit mehr als 100.000 Knoten
  • Finanzdaten: Tausende Zeitreihen gleichzeitig überwachen
  • Wissenschaftliches Rechnen: Partikelsimulationen, Visualisierungen der Fluiddynamik
  • GIS/Mapping: Rendern von Millionen geografischer Datenpunkte

Erste Schritte mit der WebGL-Visualisierung

Wenn Sie bauen:

  • Leistungsstarke Datenvisualisierungstools
  • Echtzeit-Analyse-Dashboards
  • Interaktive wissenschaftliche Computerschnittstellen
  • Systeme, die mehr als 100.000 Datenpunkte verarbeiten

Der GPU-beschleunigte Ansatz von NebulaGraph hat sich in der Produktion bewährt. Wir haben geliefert:

  • 60fps-Rendering bei mehr als 1 Mio. Punkten
  • 80 % schnellere Ladezeiten im Vergleich zu herkömmlichen Ansätzen
  • Datenwissenschaftlerfreundliche Python-API

Entdecken Sie NebulaGraph

None

Verwandte Artikel

None

Entwicklung leistungsstarker Visualisierungs- oder Datenexplorationstools? Lassen Sie uns Ihre Architektur besprechen oder abonnieren für Updates zu GPU-Computing- und Datenvisualisierungstechniken.

Bleib auf dem Laufenden

Newsletter-Synchronisierung