Architecture¶
For: developers choosing an entry point or wanting the system picture. Not needed for basic use — the Quick Start and Developer Guide stand alone.
This page explains the structure of wafermap from the outside in:
- what a user passes in,
- which public entry points handle each job,
- how the internal modules transform data, and
- how the rendered and analyzed outputs come back to the UI.
The short version is:
buildWaferMap()turns raw wafer test data into a reusableWaferMapResultrenderWaferMap()andrenderWaferGallery()turn that result into interactive UIanalyzeWaferMap()andanalyzeWaferLot()turn that result into findings and summariescreateWafermapWorker()lets you move the expensive work off the main thread
1. Top-level architecture¶
graph TB
source["Raw wafer data<br/>CSV, JSON, STDF export, API rows"]
build["buildWaferMap()"]
result["WaferMapResult"]
renderOne["renderWaferMap()"]
renderMany["renderWaferGallery()"]
analyze["analyzeWaferMap()"]
analyzeLot["analyzeWaferLot()"]
worker["createWafermapWorker()<br/>wafermap.worker"]
ui1["Interactive wafer map"]
ui2["Lot gallery"]
summary["StatsSummary"]
lotSummary["LotStatsSummary"]
source --> build
build --> result
result --> renderOne
renderOne --> ui1
result --> renderMany
renderMany --> ui2
result --> analyze
analyze --> summary
summary --> renderOne
result --> analyzeLot
analyzeLot --> lotSummary
lotSummary --> renderMany
source --> worker
What this shows
buildWaferMap() is the central data constructor. Once you have a WaferMapResult, you can render it directly, analyze it, or ship it to a Web Worker wrapper. In normal usage you do not recompute geometry for every display action; you build once, then reuse the result across rendering and analysis.
2. Package layers¶
graph LR
subgraph Core[core]
core1["wafer"]
core2["dies"]
core3["transforms"]
core4["inference"]
core5["aggregates"]
core6["classify"]
end
subgraph Renderer[renderer]
r1["buildWaferMap"]
r2["buildView"]
r3["colorMap"]
r4["colorSchemes"]
end
subgraph Canvas[canvas-adapter]
c1["renderWaferMap"]
c2["renderWaferGallery"]
c3["toCanvas"]
c4["toolbar"]
c5["summaryPanel"]
c6["canvasTheme"]
c7["insightsTab<br/>(internal)"]
c8["charts/*<br/>(internal, not a public subpath)"]
c9["metadataBadge<br/>(internal)"]
end
subgraph Stats[stats]
s1["analyzeWaferMap"]
s2["analyzeWaferLot"]
s3["regions"]
s4["clusterDetection"]
s5["chart data builders<br/>capability, boxplot, histogram,<br/>correlation, scatter, yield, binPareto"]
end
subgraph Worker[worker]
w1["wafermap.worker"]
w2["createWafermapWorker"]
end
core1 --> r1
core2 --> r1
core3 --> r1
core4 --> r1
core5 --> r1
core6 --> r1
r1 --> r2
r2 --> c3
c6 --> c3
c3 --> c1
c1 --> c4
c1 --> c5
c1 --> c7
c2 --> c7
c7 --> c8
s5 --> c8
c1 --> c9
r1 --> s1
r1 --> s2
r1 --> s5
s3 --> s1
s4 --> s1
r1 --> w1
s1 --> w1
w1 --> w2
What this shows
The codebase is organized as a stack. core is the pure foundation, renderer builds the data model used by the UI, canvas-adapter owns DOM and interaction, stats builds findings, and worker mirrors the expensive paths behind message passing. If you are deciding what to import, start with the highest layer that matches your task and drop lower only when you need more control.
insightsTab and charts/* are internal to canvas-adapter — reachable only through insights.enabled (§5.9/§6.10 in the API Reference), not an independently importable subpath. The chart panels are pure DOM/canvas rendering; the actual per-chart computations (capability, boxplot, histogram, correlation, scatter, yield, binPareto under stats/) are public and importable from /stats on their own, if you want to drive a different chart library from the same numbers.
metadataBadge is also internal to canvas-adapter — the always-visible wafer/lot metadata overlay renderWaferMap mounts bottom-left on the canvas (RenderOptions.showMetadataBadge, default true). It exists so basic wafer/lot identity (lot, wafer ID, product, test program, temperature) is never hidden behind a toolbar/Insights toggle, without costing map layout space — it's a canvas overlay, not a layout element. renderWaferGallery doesn't mount this badge at all (grid cards, detached popups, and the floating-window fallback all pass showMetadataBadge: false): it instead (a) folds a lot-wide distinct-values summary, built on buildFacetTable (stats/facets.ts), into its existing bin-legend strip, and (b) gives every per-wafer view (grid card header, popup window, floating window) its own expandable identity header for that wafer's full metadata, sharing one buildIdentityHeaderRow builder and the wireExpandToggle interaction helper (toolbar.ts) so all three read identically.
3. Data construction pipeline¶
graph TB
input["WaferMapInput<br/>results, waferConfig, dieConfig, reticleConfig, lotStack"]
normalize["Normalize inputs<br/>DieResult[] / lot stack / legacy values"]
inferGeo["Infer geometry<br/>inferWaferFromXY<br/>resolveGridPitch<br/>assignGridIndices"]
makeDies["Generate dies and wafer<br/>createWafer<br/>generateDies"]
transform["Apply transforms<br/>applyOrientation<br/>transformDies<br/>clipDiesToWafer"]
reticle["Build reticle overlay<br/>generateReticleGrid"]
output["WaferMapResult<br/>wafer, dies, view metadata,<br/>optional bin/test definitions"]
input --> normalize
normalize --> inferGeo
inferGeo --> makeDies
makeDies --> transform
transform --> output
input --> reticle
reticle --> output
What this shows
This is the part of the library that turns loose wafer test data into a structured result. You can provide as much or as little geometry as you know: explicit wafer and die sizes, or just raw x/y step coordinates. The pipeline infers the missing pieces, generates the die model, applies orientation and clipping, and attaches any reticle overlay that should travel with the map.
4. Rendering pipeline¶
graph TB
result["WaferMapResult"]
view["buildView()"]
scene["View"]
draw["toCanvas()"]
canvas["HTMLCanvasElement"]
renderOne["renderWaferMap()"]
renderMany["renderWaferGallery()"]
toolbar["Toolbar + hover + selection"]
card["Gallery card"]
result --> view
view --> scene
scene --> draw
draw --> canvas
renderOne --> view
renderOne --> toolbar
renderOne --> draw
result --> renderMany
renderMany --> card
card --> renderOne
What this shows
buildView() converts the map result into a drawable view: rectangles, overlays, labels, colors, and hover targets. toCanvas() renders that view onto a canvas and returns hit-testing and viewport information. renderWaferMap() wraps both steps with the interactive toolbar, selection, tooltips, and optional summary panel. renderWaferGallery() repeats the same display model across multiple cards and, via each card's expand button, detaches a card into its own renderWaferMap() instance in a separate window — falling back to an in-page floating window when a real separate window isn't available (e.g. inside Tauri/Electron).
5. Analysis and worker flow¶
graph LR
result["WaferMapResult"]
waferStats["analyzeWaferMap()"]
lotStats["analyzeWaferLot()"]
waferSummary["StatsSummary"]
lotSummary["LotStatsSummary"]
findings["Findings panel"]
lotPanel["Lot summary panel"]
result --> waferStats
waferStats --> waferSummary
waferSummary --> findings
result --> lotStats
lotStats --> lotSummary
lotSummary --> lotPanel
workerIn["Main thread request"]
workerScript["wafermap.worker"]
workerOut["Main thread promise resolution"]
workerResult["WaferMapResult / StatsSummary / LotStatsSummary"]
workerIn --> workerScript
workerScript --> workerOut
workerOut --> workerResult
What this shows
The analysis layer consumes the same WaferMapResult that the renderer uses. That keeps the UI and the statistics in sync. analyzeWaferMap() produces a wafer-level summary, while analyzeWaferLot() adds cross-wafer patterns and trend information. If those computations are too heavy for the main thread, the worker entry point packages the same operations behind postMessage and Promise-based calls.
The worker helper mirrors that shape: you send in a WaferMapInput or a batch of WaferMapResult objects, and the promise resolves with the computed WaferMapResult, StatsSummary, or LotStatsSummary depending on the request.
Don't confuse this with the toolbar's Insights tab (
insights.enabled, §2 above) — that's a separate chart-suite view (insightsTab/charts/*) built on thestatspackage's chart data builders, not onanalyzeWaferMap/analyzeWaferLot. The two "Analysis" names are unrelated: one produces findings/summaries, the other renders charts.
6. How to choose an entry point¶
Use these imports as your default decision tree:
import { buildWaferMap } from '@wafertools/wafermap';
import { renderWaferMap, renderWaferGallery } from '@wafertools/wafermap/render';
import { analyzeWaferMap, analyzeWaferLot } from '@wafertools/wafermap/stats';
import { createWafermapWorker } from '@wafertools/wafermap/worker';
- Use
buildWaferMap()when you have raw die rows and need a reusable result - Use
renderWaferMap()when you want one interactive wafer map - Use
renderWaferGallery()when you want a lot-level overview with per-wafer cards - Use
analyzeWaferMap()when you need spatial findings for one wafer - Use
analyzeWaferLot()when you need cross-wafer lot statistics - Use
createWafermapWorker()when the same work should happen off the main thread
If you want the deeper API surface, the API Reference lists every public type and option.