diff --git a/Examples/src/components/AppRouter/examplePaths.ts b/Examples/src/components/AppRouter/examplePaths.ts
index 7451b5a51..261512760 100644
--- a/Examples/src/components/AppRouter/examplePaths.ts
+++ b/Examples/src/components/AppRouter/examplePaths.ts
@@ -160,6 +160,7 @@ export default [
"../Examples/FeaturedApps/ScientificCharts/InteractiveWaterfallChart",
"../Examples/FeaturedApps/ScientificCharts/LiDAR3DPointCloudDemo",
"../Examples/FeaturedApps/ScientificCharts/PhasorDiagramChart",
+ "../Examples/FeaturedApps/ScientificCharts/EyeDiagramChart",
"../Examples/FeaturedApps/ScientificCharts/Semiconductors",
"../Examples/FeaturedApps/ScientificCharts/TenorCurves3D",
"../Examples/FeaturedApps/ScientificCharts/WaferAnalysis",
diff --git a/Examples/src/components/AppRouter/examples.ts b/Examples/src/components/AppRouter/examples.ts
index 8036aa658..d3d63c95c 100644
--- a/Examples/src/components/AppRouter/examples.ts
+++ b/Examples/src/components/AppRouter/examples.ts
@@ -39,6 +39,7 @@ export const MENU_ITEMS_FEATURED_APPS: TMenuItem[] = [
EXAMPLES_PAGES.featuredApps_scientificCharts_AudioAnalyzerBarsDemo,
EXAMPLES_PAGES.featuredApps_scientificCharts_WaterfallChartDemo,
EXAMPLES_PAGES.featuredApps_scientificCharts_PhasorDiagramChart,
+ EXAMPLES_PAGES.featuredApps_scientificCharts_EyeDiagramChart,
EXAMPLES_PAGES.featuredApps_scientificCharts_CorrelationPlot,
EXAMPLES_PAGES.featuredApps_scientificCharts_Semiconductors,
EXAMPLES_PAGES.featuredApps_scientificCharts_WaferAnalysis,
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/README.md b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/README.md
new file mode 100644
index 000000000..272affa35
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/README.md
@@ -0,0 +1,41 @@
+## Real-time Eye Diagram (Persistence Display)
+
+### Overview
+
+An **eye diagram** is the standard oscilloscope tool for evaluating signal integrity in high-speed serial data links. It is produced by overlaying thousands of short waveform segments — each one a 2-UI (two bit-period) window sliced from a continuous data stream — on a single time axis. When many traces accumulate, the open central regions (the "eye openings") become visible, along with the smearing caused by inter-symbol interference (ISI), timing jitter, and noise.
+
+This demo simulates an **MLT-3 (Multi-Level Transmit 3)** serial data signal — the line code used in 100BASE-TX Ethernet — entirely in the browser and renders two panels in a single SciChart surface:
+
+- **Top panel** — a live `FastLineRenderableSeries` waveform showing the most recently generated trace
+- **Bottom panel** — the accumulating `UniformHeatmapRenderableSeries` eye diagram, with an oscilloscope-style persistence display (red/yellow = frequently traversed, blue = rarely visited)
+
+MLT-3 cycles through three voltage levels (+1 V, 0 V, −1 V) via a four-state machine, producing **two stacked eye openings** per display window.
+
+### Technical Implementation
+
+**Two-panel layout** — Both panels live inside a single `SciChartSurface.createSingle` surface. Sub-charts are created with `SciChartSubSurface.createSubSurface`, positioned using `Rect` with `ESubSurfacePositionCoordinateMode.Relative`: the line chart occupies the top 28% (`Rect(0, 0, 1, 0.28)`) and the heatmap the bottom 72% (`Rect(0, 0.28, 1, 0.72)`). All axes, series, and data objects share the single `wasmContext` returned by `createSingle`.
+
+**Signal generation** — Each trace is built from a random MLT-3 state sequence (pprev, prev, curr, next) to capture both transitions in the 2-UI window:
+
+- Voltage levels: +1 V, 0 V, −1 V (four states: [+1, 0, −1, 0])
+- Per-trace **amplitude scaling** (σ = 2.5%) and **DC offset** (σ = 15 mV) for realistic trace-to-trace variation
+- **Raised-cosine transitions** spanning ~35% of one UI — matching real 100BASE-TX rise time
+- Independent per-edge **rise time variation** (σ = 2.8 samples) for realistic fuzz at crossings
+- Three crossing edges at t = 0, t = 1 UI, t = 2 UI with independent per-edge **timing jitter** (Gaussian, σ = 4.0 samples ≈ 2% of UI)
+- **Amplitude noise** (Gaussian, σ = 0.010 V) added to each sample
+
+**Heatmap accumulation** — Each trace's 400 samples are binned into a **400 × 200 accumulation grid** (time × voltage). Grid counts are log-scaled (`log1p`) before being passed to `UniformHeatmapDataSeries.setZValues()` each frame. The log scale keeps early traces visible while the hot centre saturates gracefully.
+
+**Colormap** — A custom `HeatmapColorMap` maps density to an oscilloscope thermal palette: black → dark blue → cyan → green → yellow → red.
+
+**Performance** — 50 traces (20,000 samples) are generated and binned per animation frame. The heatmap is updated via a single `setZValues()` call per frame — one GPU texture upload regardless of how many traces have accumulated. A stats overlay shows live FPS, traces/second, and total accumulated traces.
+
+### Features
+
+- **Two-panel layout** using SciChart SubCharts API — live waveform on top, persistence heatmap on bottom
+- Real-time MLT-3 eye diagram with two stacked eye openings, ISI, jitter, and noise
+- Per-trace realism: amplitude variation, DC offset, independent per-edge rise time variation
+- Oscilloscope thermal persistence display aesthetic (black → blue → cyan → yellow → red)
+- 50 traces × 60 FPS = ~3,000 traces/second accumulation
+- Stats overlay: FPS, Traces/s, Total traces
+- No controls — pure performance display
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/angular.ts b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/angular.ts
new file mode 100644
index 000000000..a934336c9
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/angular.ts
@@ -0,0 +1,32 @@
+import { Component } from "@angular/core";
+import { ScichartAngularComponent } from "scichart-angular";
+import { drawExample } from "./drawExample";
+
+@Component({
+ standalone: true,
+ imports: [ScichartAngularComponent],
+ selector: "app-eye-diagram-chart",
+ template: `
+
+
+
+ `,
+})
+export class AppComponent {
+ initChart = drawExample;
+ private controls?: { startAnimation: () => void; stopAnimation: () => void; cleanup: () => void };
+
+ onInit(initResult: Awaited>) {
+ this.controls = initResult.controls;
+ this.controls.startAnimation();
+ }
+
+ onDelete(initResult: Awaited>) {
+ this.controls?.cleanup();
+ }
+}
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/drawExample.ts b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/drawExample.ts
new file mode 100644
index 000000000..a1f9f6250
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/drawExample.ts
@@ -0,0 +1,334 @@
+import {
+ EAutoRange,
+ ESubSurfacePositionCoordinateMode,
+ FastLineRenderableSeries,
+ HeatmapColorMap,
+ I2DSubSurfaceOptions,
+ NumberRange,
+ NumericAxis,
+ Rect,
+ SciChartSubSurface,
+ SciChartSurface,
+ Thickness,
+ UniformHeatmapDataSeries,
+ UniformHeatmapRenderableSeries,
+ XyDataSeries,
+} from "scichart";
+import { appTheme } from "../../../theme";
+
+// ---------------------------------------------------------------------------
+// Signal generation helpers
+// ---------------------------------------------------------------------------
+
+/** Box-Muller transform — returns a single standard-normal random value */
+function gaussianRandom(): number {
+ let u = 0;
+ let v = 0;
+ while (u === 0) u = Math.random();
+ while (v === 0) v = Math.random();
+ return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
+}
+
+/**
+ * Generates a 400-sample MLT-3 waveform for a 2-UI window.
+ *
+ * MLT-3 cycles through voltage levels [+1, 0, -1, 0] — the signal advances to the
+ * next level on each '1' bit and holds on each '0' bit. This produces three voltage
+ * rails and two stacked eye openings when overlaid.
+ *
+ * Three crossings are placed at t=0, t=1UI, t=2UI to produce the standard
+ * oscilloscope eye pattern (two openings per display window).
+ *
+ * @returns Float32Array of 400 voltage samples
+ */
+export function generateTrace(): Float32Array {
+ const SAMPLES_PER_UI = 200;
+ const TOTAL_SAMPLES = 400;
+ const BASE_RISE_HALF = 35; // raised-cosine half-width (~35% of 1 UI — real 100BASE-TX rise time)
+ const JITTER_SIGMA = 4.0; // samples (~2% of 1 UI)
+ const NOISE_SIGMA = 0.010; // volts — per-sample Gaussian noise
+ const AMPLITUDE_SIGMA = 0.025; // ±2.5% per-trace amplitude scaling
+ const DC_OFFSET_SIGMA = 0.015; // volts — per-trace DC offset
+ const RISE_SIGMA = 2.8; // samples — per-edge rise time variation
+
+ // MLT-3 state machine: state index cycles 0→1→2→3→0, voltages [+1, 0, -1, 0]
+ const STATE_V = [1.0, 0.0, -1.0, 0.0] as const;
+
+ // Per-trace capture variations — real scope traces drift slightly in amplitude and DC level
+ const amplitudeScale = 1.0 + gaussianRandom() * AMPLITUDE_SIGMA;
+ const dcOffset = gaussianRandom() * DC_OFFSET_SIGMA;
+
+ // Start at a random state, then generate 4 consecutive levels (pprev, prev, curr, next)
+ // Each position: 50% chance to advance state (bit=1), 50% to hold (bit=0)
+ let state = Math.floor(Math.random() * 4);
+ const vLevels = Array.from({ length: 4 }, () => {
+ if (Math.random() < 0.5) state = (state + 1) % 4;
+ return STATE_V[state] * amplitudeScale + dcOffset;
+ });
+ const [vPPrev, vPrev, vCurr, vNext] = vLevels;
+
+ const out = new Float32Array(TOTAL_SAMPLES);
+
+ // Three crossing edges at t=0, t=1UI, t=2UI with independent per-edge jitter
+ const edgeA = 0 + Math.round(gaussianRandom() * JITTER_SIGMA);
+ const edgeB = SAMPLES_PER_UI + Math.round(gaussianRandom() * JITTER_SIGMA);
+ const edgeC = 2 * SAMPLES_PER_UI + Math.round(gaussianRandom() * JITTER_SIGMA);
+
+ // Independent rise time per edge — each crossing X-pattern gets its own slew, producing
+ // realistic fuzz at the crossings instead of three identical curves stacked on top of each other
+ const clampRise = (r: number) => Math.max(26, Math.min(44, r));
+ const riseA = clampRise(Math.round(BASE_RISE_HALF + gaussianRandom() * RISE_SIGMA));
+ const riseB = clampRise(Math.round(BASE_RISE_HALF + gaussianRandom() * RISE_SIGMA));
+ const riseC = clampRise(Math.round(BASE_RISE_HALF + gaussianRandom() * RISE_SIGMA));
+
+ const rc = (d: number, half: number) => 0.5 * (1 - Math.cos(Math.PI * (d + half) / (2 * half)));
+
+ for (let i = 0; i < TOTAL_SAMPLES; i++) {
+ const dA = i - edgeA, dB = i - edgeB, dC = i - edgeC;
+ let v: number;
+ if (dA >= -riseA && dA < riseA) v = vPPrev + (vPrev - vPPrev) * rc(dA, riseA);
+ else if (dB >= -riseB && dB < riseB) v = vPrev + (vCurr - vPrev) * rc(dB, riseB);
+ else if (dC >= -riseC && dC < riseC) v = vCurr + (vNext - vCurr) * rc(dC, riseC);
+ else if (i < edgeA) v = vPPrev;
+ else if (i < edgeB) v = vPrev;
+ else if (i < edgeC) v = vCurr;
+ else v = vNext;
+ out[i] = v + gaussianRandom() * NOISE_SIGMA;
+ }
+ return out;
+}
+
+/**
+ * Bins a trace into a flat Float32Array accumulation grid.
+ *
+ * Grid layout: 400 columns × 200 rows, col-major → index = col * 200 + row
+ * - col maps to sample index (0..199) scaled to 0..399
+ * - row maps to voltage (−1.5..+1.5) scaled to 0..199
+ */
+export function binTrace(grid: Float32Array, trace: Float32Array): void {
+ const COLS = 400;
+ const ROWS = 200;
+ const V_MIN = -1.5;
+ const V_MAX = 1.5;
+ const V_RANGE = V_MAX - V_MIN;
+
+ for (let i = 0; i < trace.length; i++) {
+ // Map sample index to column (0..COLS-1)
+ const col = Math.round((i / (trace.length - 1)) * (COLS - 1));
+
+ // Map voltage to row (0..ROWS-1); clamp to bounds
+ const rowF = ((trace[i] - V_MIN) / V_RANGE) * (ROWS - 1);
+ const row = Math.max(0, Math.min(ROWS - 1, Math.round(rowF)));
+
+ grid[col * ROWS + row] += 1;
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Main drawExample function
+// ---------------------------------------------------------------------------
+
+export async function drawExample(rootElement: string | HTMLDivElement) {
+ // Single surface with two sub-charts avoids multi-surface init complexity
+ const { sciChartSurface: mainSurface, wasmContext } = await SciChartSurface.createSingle(
+ rootElement,
+ { theme: appTheme.SciChartJsTheme }
+ );
+
+ // Main surface axes (required by SciChart even when hidden)
+ mainSurface.xAxes.add(new NumericAxis(wasmContext, { isVisible: false, id: "mainX" }));
+ mainSurface.yAxes.add(new NumericAxis(wasmContext, { isVisible: false, id: "mainY" }));
+
+ // ── Top sub-chart: live waveform (top 28%) ────────────────────────────────
+
+ const lineSubOptions: I2DSubSurfaceOptions = {
+ theme: appTheme.SciChartJsTheme,
+ position: new Rect(0, 0, 1, 0.28),
+ coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
+ padding: Thickness.fromNumber(2),
+ };
+ const lineSurface = SciChartSubSurface.createSubSurface(mainSurface, lineSubOptions);
+
+ lineSurface.xAxes.add(new NumericAxis(wasmContext, {
+ isVisible: false,
+ visibleRange: new NumberRange(0, 2),
+ autoRange: EAutoRange.Never,
+ }));
+
+ lineSurface.yAxes.add(new NumericAxis(wasmContext, {
+ axisTitle: "Voltage (V)",
+ axisTitleStyle: { fontSize: 11 },
+ labelStyle: { fontSize: 10 },
+ visibleRange: new NumberRange(-1.5, 1.5),
+ autoRange: EAutoRange.Never,
+ drawMajorGridLines: true,
+ drawMinorGridLines: false,
+ }));
+
+ const TRACE_LEN = 400;
+ const xTrace = Array.from({ length: TRACE_LEN }, (_, i) => (i / (TRACE_LEN - 1)) * 2);
+ const yBuffer = new Array(TRACE_LEN).fill(0);
+
+ const lineDs = new XyDataSeries(wasmContext, { xValues: xTrace, yValues: yBuffer });
+ lineSurface.renderableSeries.add(
+ new FastLineRenderableSeries(wasmContext, {
+ dataSeries: lineDs,
+ stroke: "#00E5FF",
+ strokeThickness: 1.5,
+ })
+ );
+
+ // ── Bottom sub-chart: heatmap eye diagram (bottom 72%) ────────────────────
+
+ const heatSubOptions: I2DSubSurfaceOptions = {
+ theme: appTheme.SciChartJsTheme,
+ position: new Rect(0, 0.28, 1, 0.72),
+ coordinateMode: ESubSurfacePositionCoordinateMode.Relative,
+ padding: Thickness.fromNumber(2),
+ };
+ const heatSurface = SciChartSubSurface.createSubSurface(mainSurface, heatSubOptions);
+
+ heatSurface.xAxes.add(new NumericAxis(wasmContext, {
+ axisTitle: "Time (UI)",
+ axisTitleStyle: { fontSize: 11 },
+ labelStyle: { fontSize: 10 },
+ visibleRange: new NumberRange(0, 2),
+ autoRange: EAutoRange.Never,
+ drawMajorGridLines: false,
+ drawMinorGridLines: false,
+ }));
+
+ heatSurface.yAxes.add(new NumericAxis(wasmContext, {
+ axisTitle: "Voltage (V)",
+ axisTitleStyle: { fontSize: 11 },
+ labelStyle: { fontSize: 10 },
+ visibleRange: new NumberRange(-1.15, 1.15),
+ autoRange: EAutoRange.Never,
+ drawMajorGridLines: false,
+ drawMinorGridLines: false,
+ }));
+
+ const COLS = 400;
+ const ROWS = 200;
+ const grid = new Float32Array(COLS * ROWS);
+ const zValuesLog: number[][] = Array.from({ length: ROWS }, () => new Array(COLS).fill(0));
+
+ const heatDs = new UniformHeatmapDataSeries(wasmContext, {
+ xStart: 0,
+ xStep: 2 / (COLS - 1),
+ yStart: -1.15,
+ yStep: 2.3 / (ROWS - 1),
+ zValues: zValuesLog,
+ });
+
+ heatSurface.renderableSeries.add(
+ new UniformHeatmapRenderableSeries(wasmContext, {
+ dataSeries: heatDs,
+ useLinearTextureFiltering: true,
+ colorMap: new HeatmapColorMap({
+ minimum: 0,
+ maximum: 8,
+ gradientStops: [
+ { offset: 0, color: "#000000" },
+ { offset: 0.15, color: "#0000FF" },
+ { offset: 0.4, color: "#00FFFF" },
+ { offset: 0.6, color: "#00FF00" },
+ { offset: 0.8, color: "#FFFF00" },
+ { offset: 1, color: "#FF0000" },
+ ],
+ }),
+ })
+ );
+
+ // ── Stats overlay ─────────────────────────────────────────────────────────
+
+ const container = typeof rootElement === "string"
+ ? document.getElementById(rootElement)
+ : rootElement;
+
+ if (container) container.style.position = "relative";
+
+ const statsDiv = document.createElement("div");
+ statsDiv.style.cssText = [
+ "position:absolute", "top:8px", "right:8px",
+ "font-family:monospace", "font-size:12px", "color:#ffffff",
+ "background:rgba(0,0,0,0.5)", "padding:4px 8px",
+ "border-radius:4px", "pointer-events:none", "z-index:100",
+ ].join(";");
+ statsDiv.textContent = "FPS: -- | Traces/s: -- | Total: 0";
+
+ if (container) container.appendChild(statsDiv);
+
+ // ── Animation loop ────────────────────────────────────────────────────────
+
+ let rafHandle: number | null = null;
+ let totalTraces = 0;
+ let frameCount = 0;
+ let lastStatsTime = performance.now();
+ const TRACES_PER_FRAME = 50;
+
+ function animate() {
+ let lastTrace: Float32Array | null = null;
+
+ for (let t = 0; t < TRACES_PER_FRAME; t++) {
+ const trace = generateTrace();
+ binTrace(grid, trace);
+ lastTrace = trace;
+ }
+ totalTraces += TRACES_PER_FRAME;
+
+ // Replace line chart data with the last trace from this batch
+ if (lastTrace) {
+ for (let i = 0; i < TRACE_LEN; i++) {
+ yBuffer[i] = lastTrace[i];
+ }
+ lineDs.clear();
+ lineDs.appendRange(xTrace, yBuffer);
+ }
+
+ // Update heatmap
+ for (let col = 0; col < COLS; col++) {
+ for (let row = 0; row < ROWS; row++) {
+ zValuesLog[row][col] = Math.log1p(grid[col * ROWS + row]);
+ }
+ }
+ heatDs.setZValues(zValuesLog);
+
+ frameCount++;
+ if (frameCount % 30 === 0) {
+ const now = performance.now();
+ const elapsed = (now - lastStatsTime) / 1000;
+ const fps = Math.round(30 / elapsed);
+ const tracesPerSec = Math.round((30 * TRACES_PER_FRAME) / elapsed);
+ lastStatsTime = now;
+ statsDiv.textContent = `FPS: ${fps} | Traces/s: ${tracesPerSec} | Total: ${totalTraces.toLocaleString()}`;
+ }
+
+ rafHandle = requestAnimationFrame(animate);
+ }
+
+ function startAnimation() {
+ if (rafHandle !== null) return;
+ rafHandle = requestAnimationFrame(animate);
+ }
+
+ function stopAnimation() {
+ if (rafHandle !== null) {
+ cancelAnimationFrame(rafHandle);
+ rafHandle = null;
+ }
+ }
+
+ startAnimation();
+
+ function cleanup() {
+ stopAnimation();
+ if (container) container.removeChild(statsDiv);
+ mainSurface.delete();
+ }
+
+ return {
+ sciChartSurface: mainSurface,
+ controls: { startAnimation, stopAnimation, cleanup },
+ };
+}
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/exampleInfo.tsx b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/exampleInfo.tsx
new file mode 100644
index 000000000..7bd94710b
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/exampleInfo.tsx
@@ -0,0 +1,70 @@
+import { createExampleInfo } from "../../../exampleInfoUtils";
+import { IExampleMetadata } from "../../../IExampleMetadata";
+
+const metaData: IExampleMetadata =
+ //// This metadata is computer generated - do not edit!
+ {
+ reactComponent: "EyeDiagramChart",
+ id: "featuredApps_scientificCharts_EyeDiagramChart",
+ imagePath: "javascript-eye-diagram-chart.jpg",
+ description:
+ "Demonstrates a real-time **Eye Diagram** (persistence display) using SciChart.js heatmap rendering. Simulates an **MLT-3** signal (three voltage levels, used in 100BASE-TX Ethernet) — thousands of traces accumulate into a 2D density grid, producing the two-eye oscilloscope persistence pattern.",
+ tips: [],
+ frameworks: {
+ javascript: {
+ subtitle:
+ "Demonstrates a real-time **Eye Diagram** (persistence display) using SciChart.js heatmap rendering. Simulates an **MLT-3** signal (three voltage levels, used in 100BASE-TX Ethernet) — thousands of traces accumulate into a 2D density grid, producing two stacked eye openings with the classic oscilloscope glow.",
+ title: "Real-time Eye Diagram Chart Example",
+ pageTitle: "Real-time Eye Diagram (Persistence Display)",
+ metaDescription:
+ "See a real-time Eye Diagram rendered with SciChart.js. Simulates an MLT-3 signal — thousands of waveform traces accumulate into a heatmap density grid — like a real oscilloscope persistence display.",
+ markdownContent:
+ "## Real-time Eye Diagram - JavaScript\n\n### Overview\nThis example demonstrates a **real-time Eye Diagram** (persistence display) using SciChart.js in JavaScript. An eye diagram is the standard tool for evaluating signal integrity in high-speed serial data links — it is produced by overlaying thousands of short waveform segments on a shared time axis, revealing the eye opening, jitter, noise, and inter-symbol interference (ISI) of the signal. This demo simulates an **MLT-3 (Multi-Level Transmit 3)** signal, the line code used in 100BASE-TX Ethernet, which cycles through three voltage levels (+1 V, 0 V, −1 V) to produce **two stacked eye openings** per display window.\n\n### Technical Implementation\nThe layout uses [SciChartSurface.createSingle()](https://www.scichart.com/documentation/js/v5/2d-charts/surface/new-scichart-surface/#scichartsurfacecreatesingle) with two sub-charts created via `SciChartSubSurface.createSubSurface`. The top sub-chart (`Rect(0, 0, 1, 0.28)` relative) hosts a [FastLineRenderableSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/line-chart/) showing the live waveform; the bottom sub-chart (`Rect(0, 0.28, 1, 0.72)`) hosts the [UniformHeatmapRenderableSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) eye diagram. Both panels share a single `wasmContext`. Each animation frame, 50 simulated MLT-3 traces are generated with raised-cosine edge shaping (~35% of one UI), per-trace amplitude variation (σ = 2.5%) and DC offset (σ = 15 mV), independent per-edge timing jitter (σ = 4 samples ≈ 2% UI), and Gaussian amplitude noise (σ = 0.010 V). Trace samples are binned into a **400 × 200 accumulation grid** and passed to [UniformHeatmapDataSeries.setZValues()](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) each frame — a single WebGL texture upload regardless of how many traces have accumulated.\n\n### Features and Capabilities\nThe [UniformHeatmapRenderableSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) renders the density grid with a custom [HeatmapColorMap](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) using an oscilloscope thermal palette (black → dark blue → cyan → green → yellow → red) that produces the classic scope persistence glow. A `log1p` scale keeps sparse edges visible while saturating the hot centre. Per-trace realism (amplitude scaling, DC offset, independent rise time variation) produces authentic oscilloscope fuzz at crossings and thick voltage rails. A live stats overlay shows FPS, traces/second, and total accumulated traces.\n\n### Integration and Best Practices\nThe implementation uses a `requestAnimationFrame` loop with a clean start/stop/cleanup API. Using `createSingle` with sub-charts instead of multiple `SciChartSurface.create` calls avoids WASM context conflicts and eliminates fragile DOM measurement for panel sizing. Resource cleanup (cancelling the rAF loop, removing the DOM overlay, calling `mainSurface.delete()`) is encapsulated in a single `cleanup()` function returned from `drawExample`, following the pattern used across all SciChart.js examples.",
+ },
+ react: {
+ subtitle:
+ "Demonstrates a real-time **Eye Diagram** (persistence display) using SciChart.js heatmap rendering. Simulates an **MLT-3** signal (three voltage levels, used in 100BASE-TX Ethernet) — thousands of traces accumulate into a 2D density grid, producing two stacked eye openings with the classic oscilloscope glow.",
+ title: "Real-time Eye Diagram Chart Example",
+ pageTitle: "Real-time Eye Diagram (Persistence Display)",
+ metaDescription:
+ "See a real-time Eye Diagram rendered with React and SciChart.js. Simulates an MLT-3 signal — thousands of waveform traces accumulate into a heatmap density grid — like a real oscilloscope persistence display.",
+ markdownContent:
+ "## Real-time Eye Diagram - React\n\n### Overview\nThis React example demonstrates a **real-time Eye Diagram** (persistence display) built with SciChart.js. It simulates an **MLT-3 (Multi-Level Transmit 3)** serial data signal — the line code used in 100BASE-TX Ethernet — and renders two panels: a live waveform line chart on top and an accumulating 2D density heatmap on the bottom, producing the oscilloscope persistence glow that signal-integrity engineers use to evaluate jitter, noise, and eye opening. MLT-3's three voltage levels (+1 V, 0 V, −1 V) create **two stacked eye openings** visible once enough traces have accumulated.\n\n### Technical Implementation\nThe chart initialises via `` where `drawExample` creates a single surface with `SciChartSurface.createSingle` and two sub-charts via `SciChartSubSurface.createSubSurface`. The top sub-chart (`Rect(0, 0, 1, 0.28)`, relative coordinates) hosts a [FastLineRenderableSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/line-chart/) for the live waveform; the bottom sub-chart (`Rect(0, 0.28, 1, 0.72)`) hosts a [UniformHeatmapDataSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) backed by a 400 × 200 accumulation grid. Each frame, 50 MLT-3 traces (raised-cosine transitions ~35% UI, per-trace amplitude variation σ = 2.5%, DC offset σ = 15 mV, timing jitter σ = 4 samples, amplitude noise σ = 0.010 V) are generated and binned. The heatmap is updated via `setZValues()` — one GPU upload per frame. The `onDelete` prop calls `controls.cleanup()` following [React cleanup patterns for SciChart.js](https://www.scichart.com/documentation/js/v5/get-started/tutorials-react/tutorial-01-setting-up-project-with-scichart-react/).\n\n### Features and Capabilities\nA custom [HeatmapColorMap](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) using an oscilloscope thermal palette (black → dark blue → cyan → green → yellow → red) with `log1p` density scaling produces the classic scope persistence glow. Per-trace realism (amplitude scaling, DC offset, independent per-edge rise time) adds authentic oscilloscope fuzz. A live overlay shows FPS, traces/second, and total accumulated traces.\n\n### Integration and Best Practices\nThe component is intentionally minimal — no React state is needed because the animation lifecycle is entirely managed by the `controls` object returned from `drawExample`. Using `createSingle` with sub-charts instead of multiple surfaces avoids WASM context conflicts and eliminates fragile DOM measurement for panel sizing. This pattern keeps the React wrapper decoupled from the SciChart initialisation logic and makes the same `drawExample` function reusable across React, Angular, and vanilla JavaScript targets.",
+ },
+ angular: {
+ subtitle:
+ "Demonstrates a real-time **Eye Diagram** (persistence display) using SciChart.js heatmap rendering. Simulates an **MLT-3** signal (three voltage levels, used in 100BASE-TX Ethernet) — thousands of traces accumulate into a 2D density grid, producing two stacked eye openings with the classic oscilloscope glow.",
+ title: "Real-time Eye Diagram Chart Example",
+ pageTitle: "Real-time Eye Diagram (Persistence Display)",
+ metaDescription:
+ "See a real-time Eye Diagram rendered with Angular and SciChart.js. Simulates an MLT-3 signal — thousands of waveform traces accumulate into a heatmap density grid — like a real oscilloscope persistence display.",
+ markdownContent:
+ "## Real-time Eye Diagram - Angular\n\n### Overview\nThis Angular example demonstrates a **real-time Eye Diagram** (persistence display) using SciChart.js and the [scichart-angular](https://www.npmjs.com/package/scichart-angular) package. It simulates an **MLT-3 (Multi-Level Transmit 3)** serial data signal — the line code used in 100BASE-TX Ethernet — and renders two panels: a live waveform line chart on top and an accumulating heatmap density display on the bottom, replicating the oscilloscope persistence effect used in signal-integrity analysis. MLT-3's four-state voltage machine (+1 V → 0 V → −1 V → 0 V) produces **two stacked eye openings** per display window.\n\n### Technical Implementation\nThe standalone Angular component uses `` to initialise the SciChart surface. The `drawExample` function creates a single surface with `SciChartSurface.createSingle` and two sub-charts via `SciChartSubSurface.createSubSurface`. The top sub-chart (`Rect(0, 0, 1, 0.28)`, relative coordinates) hosts a live [FastLineRenderableSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/line-chart/) waveform; the bottom sub-chart (`Rect(0, 0.28, 1, 0.72)`) hosts a [UniformHeatmapDataSeries](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) backed by a 400 × 200 accumulation grid. Each animation frame, 50 MLT-3 traces (raised-cosine transitions ~35% UI, per-trace amplitude variation σ = 2.5%, DC offset σ = 15 mV, timing jitter σ = 4 samples, amplitude noise σ = 0.010 V) are generated and binned, then pushed to the GPU via `setZValues()`. The animation starts automatically — lifecycle cleanup is managed via the `(onDelete)` event binding.\n\n### Features and Capabilities\nA custom [HeatmapColorMap](https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/) with `log1p` density scaling maps the accumulation grid to an oscilloscope thermal palette (black → dark blue → cyan → green → yellow → red). Frequently-traversed regions glow red (the eye centre and voltage rails), while sparse edge crossings fade to blue. Per-trace realism (amplitude scaling, DC offset, independent rise time variation) adds authentic oscilloscope fuzz at crossings. A live stats overlay displays FPS, traces/second, and total accumulated traces.\n\n### Integration and Best Practices\nThe implementation follows Angular standalone component best practices. Using `createSingle` with sub-charts instead of multiple surfaces avoids WASM context conflicts and eliminates fragile DOM measurement for panel sizing. Resource cleanup (stopping the rAF loop, removing the DOM overlay, and calling `mainSurface.delete()`) is handled in a single `controls.cleanup()` call from the `(onDelete)` handler, preventing memory leaks when the component is destroyed.",
+ },
+ },
+ documentationLinks: [
+ {
+ href: "https://www.scichart.com/documentation/js/v5/2d-charts/chart-types/uniform-heatmap-renderable-series/uniform-heatmap-chart-type/",
+ title: "Uniform Heatmap Chart Type documentation",
+ linkTitle: "Uniform Heatmap Chart Documentation",
+ },
+ ],
+ path: "eye-diagram-chart",
+ metaKeywords: "eye diagram, persistence, oscilloscope, MLT-3, signal integrity, heatmap, real-time, performance, javascript, webgl",
+ onWebsite: true,
+ filepath: "FeaturedApps/ScientificCharts/EyeDiagramChart",
+ thumbnailImage: "javascript-eye-diagram-chart.jpg",
+ sandboxConfig: {
+ infiniteLoopProtection: false,
+ hardReloadOnChange: false,
+ view: "browser",
+ },
+ markdownContent: null,
+ pageLayout: "default",
+ extraDependencies: {},
+ isNew: true,
+ };
+//// End of computer generated metadata
+
+export const eyeDiagramChartExampleInfo = createExampleInfo(metaData);
+export default eyeDiagramChartExampleInfo;
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/index.tsx b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/index.tsx
new file mode 100644
index 000000000..10426a96b
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/index.tsx
@@ -0,0 +1,17 @@
+import { SciChartReact, TResolvedReturnType } from "scichart-react";
+import commonClasses from "../../../styles/Examples.module.scss";
+import { drawExample } from "./drawExample";
+
+export default function EyeDiagramChart() {
+ return (
+
+ drawExample(rootElementId)}
+ onDelete={(initResult: TResolvedReturnType) => {
+ initResult.controls.cleanup();
+ }}
+ />
+
+ );
+}
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/javascript-eye-diagram-chart.jpg b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/javascript-eye-diagram-chart.jpg
new file mode 100644
index 000000000..8968d64e9
Binary files /dev/null and b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/javascript-eye-diagram-chart.jpg differ
diff --git a/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/vanilla.ts b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/vanilla.ts
new file mode 100644
index 000000000..5e4b7fe5e
--- /dev/null
+++ b/Examples/src/components/Examples/FeaturedApps/ScientificCharts/EyeDiagramChart/vanilla.ts
@@ -0,0 +1,13 @@
+import { drawExample } from "./drawExample";
+
+const create = async () => {
+ const { controls } = await drawExample("chart");
+
+ const destructor = () => {
+ controls.cleanup();
+ };
+
+ return destructor;
+};
+
+create();