Live demo: playback of a real 256-subcarrier CSI heatmap

By Wu Jun · Updated 2026-11-02 · 5 min read

This playback uses a real capture (GL-MT6000 / MT7986, 5 GHz HE80, 256 subcarriers, about 60 s). The browser loads a downsampled view of complete measurement groups (347 frames x 64 subcarriers) so it plays smoothly. The source has 2,777 chain records, 347 complete bit-15 groups and one unterminated tail record. The anonymised source and capture method are public [1] [2].

time →vertical axis: subcarriers (low at bottom)
green: per-measurement motion metricblue: playback position

How to read it

Amplitude view (default)

Doppler / time-frequency view

The green curve below is shown in both views: it is the motion metric (mean absolute change between consecutive measurements), a cheap "is anything moving?" signal.

A short exercise for first-time readers

Use the replay as an instrument, not as a decorative animation:

  1. Pause on the first frame and inspect the subcarrier axis. The top and bottom are not “good” and “bad”; they are different parts of the measured channel.
  2. Start at 1x speed and watch the heatmap without changing the view. Note whether changes are local to a few subcarriers or form a vertical band across many of them.
  3. Switch to the Doppler view at the same position. A feature that looked like motion in the heatmap should produce changing relative energy somewhere in the low-frequency region. If it does not, do not force an interpretation.
  4. Increase the playback speed. This changes only presentation time. It does not increase the capture rate or extend the measurable Doppler range.
  5. Compare the green motion curve with what you observed. A flat section is evidence about this motion metric on this recording; it is not proof that the room was empty.

The useful output of the exercise is a reproducible observation: the selected timestamp, the visible pattern, the view and the expected physical explanation. A screenshot without that context is weak evidence, especially when the colour scale has been normalised.

Why the browser uses a smaller representation

The source contains 256 subcarriers, but the page renders 64 vertical bins and computes a 32-bin Doppler display. Reducing the display grid keeps the page responsive and makes broad changes easier to see. It does not replace the source file or change the checksum. Anyone testing a parser or a feature-extraction method should use the original capture and the published toolkit rather than scraping the canvas image.

The animation is also not a live sensor feed. It does not connect to a router, open a WebSocket, request microphone or camera access, or upload the selected file. The page remains useful as a static visual explanation when JavaScript is unavailable, but the interactive canvases naturally require JavaScript.

Where this data comes from

Item Value
Capture device GL.iNet GL-MT6000 (MT7986A + MT7976), OpenWrt + mt76 CSI
Band / width 5 GHz / HE80 / ch44
Subcarriers / chains 256 / 2 Tx x 4 Rx
Measurement rate about 5.79 complete groups/s (→ Doppler observable up to 0–2.89 Hz)
Raw data 2,777 chain records / 347 complete groups / one tail fragment, 2.8 MB, with SHA-256
In the browser amplitude 347 frames x 64 subcarriers; Doppler 347 frames x 32 frequency bins (same timeline, background-subtracted + percentile-normalised)
Integrity only bit-15 complete groups are rendered; the unterminated tail is excluded

What the replay can and cannot show

Can show Cannot show
how one real capture evolves over time that a model is accurate in every room
whether parsing, downsampling and visualisation agree that colour is absolute dBm or absolute power
relative energy in slow-motion/respiration bands walking micro-Doppler from a 5.79 Hz sample rate
that the source file is unchanged via SHA-256 that "no motion detected" means an empty room

The browser does not connect to your router or upload data; the static assets are rendered locally. Use the 10-second sample to validate a parser, or the source and manifest above to recompute the page statistics.

Recompute the evidence from the raw file

A reviewable workflow is deliberately boring: verify the file, validate the container, then compute one number from the bytes and compare it with the manifest.

# 1. Verify the downloaded archive/checksum record
shasum -a 256 -c SHA256SUMS

# 2. Reject framing, chain and timestamp defects before plotting
python3 scripts/check_csi_capture.py csi_demo_60s_anonymized.bin --format markdown

# 3. Parse complete bit-15 groups and recompute the summary
python3 scripts/analyze_csi_capture.py csi_demo_60s_anonymized.bin

The expected top-level observations are 2,777 chain records, 347 closed measurement groups and one tail fragment. Those values must be derived from the file; changing the prose on this page cannot change them. A mismatch is a reason to inspect the parser version and the container offset before trusting any heatmap or Doppler figure.

The toolkit and quality report are public under MIT [1]. For the full ten-minute capture and the dataset DOI, continue to tools and data or the dataset page.

Reproduce it yourself: router CSI guide · Python pipeline · tools and data

References and data sources

Every external claim carries a source and an evidence grade (grading scheme). Grade E marks first-party, reproducible measurements from this project.

  1. Grade E · first-party measurement This project's 10-minute raw capture (57,293 records / 600.0 s, SHA-256 00373f83…95e2)
    Router CSI Lab · 2026
    okwujun.pw/en/datasets/ · verified: Reassembled chunks verified byte-identical (2026-10-11) · The local raw master has SHA-256 00373f83…95e2; the public de-identified derivative has SHA-256 527466ce…a440. They must not be confused.
  2. Grade E · first-party measurement Router CSI Lab — GL-MT6000 (MT7986) Wi-Fi CSI dataset, 10 s sample + 10 min capture
    Router CSI Lab · 2026
    doi:10.5281/zenodo.23434789 · verified: CC BY 4.0; SHA-256 published; Zenodo DOI 10.5281/zenodo.23434789 · The dataset has a Zenodo DOI (10.5281/zenodo.23434789) and can be cited directly.