Demo — not a medical device — synthetic data only

Radiology reading workstation · Portfolio project

Read, measure, compare, sign — and trust what is on screen.

A reading workstation in the browser, on synthetic CT: an in-house WebGPU renderer for slices, MPR and slabs, priors and nodule tracking, AI findings a human has to settle, structured reports with pre-sign checks, and an audit trail nobody can rewrite.

Demo — not a medical device — synthetic data only. Work in progress.

The Tomos viewer on a synthetic chest CT: axial, sagittal and coronal reformats and a 10 mm MIP slab in a 2×2 layout, the patient's identity on every viewport, three AI findings pending review on the right, and the tracked nodule's volume-doubling time of 291 days and Lung-RADS 4B
01 · The need

Many studies, one reader, no room for doubt.

A radiologist reads study after study through a session, and signs each one. Three things decide whether what they sign is right: the patient, the image, the numbers.

01

The right patient

Name, ID, date of birth and sex on every viewport, and a prior compared only once its Patient ID and issuer match. Similar names and identical birth dates are a known wrong-patient pattern.

02

The whole image

A slice still on its way is drawn as hatched no-data, never as the previous image; a partial series says how many slices it holds, and is not used as evidence.

03

Numbers to trust, a human who signs

Measurements in millimetres only when the spacing says so; AI findings that wait for the reader; a report signed by a person, kept as signed, corrected by addendum.

02 · The rules

What the workstation holds to.

A few rules every screen keeps, because a viewer that is wrong quietly is worse than one that says it cannot show.

01

Identity before pixels

A prior is matched on Patient ID and issuer before a single pixel of it is fetched. A mismatch blocks the comparison, with no override, and lands in the audit trail.

02

Nothing auto-accepted

Every AI finding stays pending until a human accepts, edits or rejects it. Signing is disabled while any is pending, and the AI has no signing identity at all.

03

Measurements that say what they are

In pixels, marked UNCALIBRATED, when pixel spacing is missing; disabled on slabs and on incomplete series, where a length would mean nothing.

04

A demo, and it says so

Not a medical device, no compliance claimed, synthetic data only: generated studies, invented demographics, zero external calls, and a banner on every screen.

03 · The workstation

Built and running.

Stills of the live workstation on its synthetic studies: the viewer, the worklist, a prior compared, a wrong patient refused, a report before signing, the audit trail.

The worklist: eight chest and head CT studies sorted by priority then time left to the SLA, urgent ones first, a study locked by another reader, AI suggestion counts, and the selected patient's preview on the right
The worklist, by priority then time to SLA
The viewer in a mediastinal window, W 350 L 50: the contrast-filled aorta and heart in axial, sagittal and coronal reformats, the MIP slab marked as a projection with measurements disabled
Mediastinal window across the three planes
The current study beside its prior from 399 days earlier, both complete and verified on ID, issuer, date of birth and sex; below, three tracked nodules, N1 growing from 113 to 292 mm³ with a volume-doubling time of 291 days, Lung-RADS 4B awaiting the reader's confirmation
A prior compared, a nodule tracked: VDT 291 d, Lung-RADS 4B
The comparison blocked: the selected prior belongs to MARTIN, Claude, TMS-00419, male, not MARTIN, Claire, TMS-00418; the prior's images are withheld, the check cannot be overridden, and the event is written to the audit trail
A wrong-patient prior, refused with no override
The structured chest CT report beside its pre-sign checks: identity to re-confirm, three AI suggestions still pending, findings not yet linked to evidence; the note that the AI has no signing identity
The report and its pre-sign checks
The audit trail: each event with its actor, object and the SHA-256 of the previous one, the chain verified over 103 events, the COMPARE_BLOCKED event open on the right
The audit trail, hash-chained and verified
04 · Choices

Weighed, chosen, and why.

Each key decision, with the alternatives, the choice and its reason. Each one is written down as a decision record in the repository.

  1. 01

    Drawing the volumes

    Weighed
    Cornerstone3D / OHIF · vtk.js · in-house WebGL2 · in-house WebGPU
    Chosen
    An in-house WebGPU renderer: one r16sint 3D texture per volume, one WGSL shader for slices, MPR and MIP / MinIP / average slabs.
    Because
    Compute shaders for histograms, ROI statistics and slabs, and the rendering is the part worth showing. The industry's viewers run on WebGL2 for conservative workstations: Tomos says so on an explicit unsupported screen, and the WebGL2 fallback is next.
  2. 02

    Reading DICOM

    Weighed
    dicom-parser · dcmjs · an in-house reader
    Chosen
    An in-house Part 10 reader of about 250 lines in a worker pool: explicit and implicit VR little endian, uncompressed 16-bit greyscale.
    Because
    It refuses by name — no DICM magic, compressed, big-endian, truncated — and the viewer turns each refusal into a state on the viewport instead of a guess.
  3. 03

    Keeping a signed report signed

    Weighed
    Checks in the API · constraints in the database
    Chosen
    PostgreSQL triggers: a signed report cannot be updated; a correction is a signed addendum.
    Because
    The rule holds whatever code reaches the table, not only the code that remembers it.
  4. 04

    Recording who did what

    Weighed
    An ordinary log table · an append-only, hash-chained trail
    Chosen
    Each audit event stores the SHA-256 of the previous one; the API's database role can only INSERT and SELECT it.
    Because
    Any edit or deletion breaks the chain, and the workstation verifies it on screen.
  5. 05

    Where the images come from

    Weighed
    Public patient datasets · a procedural generator
    Chosen
    A dependency-free CT generator: 17 studies, 3,048 DICOM files, realistic HU, noise per kernel, nodules planted with their ground truth.
    Because
    No patient data anywhere, a known truth to test against, and the edge cases on demand: partial, corrupted, uncalibrated, non-uniform spacing, a wrong-patient prior.
05 · How it's built

From the generated scanner to the signed report.

A generator that stands in for the scanner, an API that stands in for the PACS and the AI, and a viewer that draws on the GPU: the boundaries a real department would draw.

01

Generator

Node, no dependencies: anatomy with vascular and bronchial trees, lung ≈ −840 HU, fat ≈ −100, contrast aorta ≈ 205, written as DICOM Part 10 with its ground truth.

02

API

NestJS 12 on PostgreSQL: a simulated DICOMweb PACS, mock AI inference, measurements recomputed server-side, nodule tracking and Lung-RADS, reports, the audit chain.

03

Viewer

Next 16 and React 19: DICOM parsed in a worker pool, volumes on the GPU, frames drawn outside React from a zustand store; first image in 899 ms against a 1 s budget.

04

Server

PM2 and nginx on my own server, DICOM served from disk, Let's Encrypt; deploys gated by the full verify suite, registered in Zeus, watched by Iknos.

06 · Status

What is built, and what is next.

Live as a demo, built ticket by ticket, each decision written down as it is taken.

Built · reading

  • ViewerSlices, axial / sagittal / coronal MPR and MIP / MinIP / average slabs on WebGPU, window/level presets, the HU probe, measurements.
  • Worklist and priorsPriority and SLA ordering, study locks, prior comparison with linked scroll, the wrong-patient block.
  • Nodule trackingVolume from three diameters, volume-doubling time, a Lung-RADS v2022 rule engine the reader confirms.

Built · signing

  • AI reviewMock inference whose findings stay pending until a human accepts, edits or rejects them.
  • ReportsA structured template, pre-sign checks, immutable signed reports and signed addenda.
  • Audit and regulatoryThe hash-chained trail verified on screen, and a page on what a real device would have to satisfy.

Next

  • WebGL2 fallbackFor workstations without WebGPU, behind the same renderer shape.
  • Key imagesMarked in the viewer, cited as evidence in the report.
  • More templates, volume renderingReport templates beyond the chest CT, and a 3D rendering of the volume.

Read a study,
on a synthetic chest CT.

Scroll the MPR, measure a nodule, compare it with last year's scan, then try to sign with an AI finding still pending. Then open the wrong patient's prior and watch it refuse.