Files
clawhdf5/examples/wasm-viewer
osobhandClaude Opus 5.5 b58d61cfb7 test(wasm): accept a zstd read when the build has the filter
cargo test --workspace unifies clawhdf5-format/zstd on (another member
enables it), so the native interop test read the Zstd dataset that the
wasm build refuses. The fixture now records its values plus the error
the wasm build must give; the native test accepts either, the Node test
of the real wasm package still requires the error.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-26 00:06:26 -05:00
..

HDF5 viewer in the browser

A single page that opens an HDF5 or NetCDF-4 file entirely in the browser with clawhdf5-wasm (clawhdf5's reader compiled to WebAssembly): drop a file, browse its groups, and look at a dataset's type, shape, attributes and values (a 50 x 12 window at a time, read as a hyperslab, with the leading dimensions of a 3-D+ dataset held at chosen indices). The file never leaves the page.

Build and open

rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.129   # must equal the crate version; build.sh checks
bash examples/wasm-viewer/build.sh                  # writes examples/wasm-viewer/pkg/ (not committed)
python3 -m http.server -d examples/wasm-viewer 8000 # wasm cannot load from file://

Then open http://localhost:8000/. ?file=<url>&path=<object> opens a file from a URL (same origin, or one serving CORS headers) and selects an object in it, e.g. ?file=data/run1.h5&path=/results/energy.

JavaScript API

import init, { open } from "./pkg/clawhdf5_wasm.js";
await init();
const f = open(new Uint8Array(await blob.arrayBuffer()));
f.list("/");                          // [{ name, kind: "group" | "dataset" }], groups first
f.info("/grid");                      // { shape, maxshape, dtype, elementShape }
f.attrs("/grid");                     // [{ name, value, dtype }]
f.read("/grid");                      // { shape, dtype, data }
f.readHyperslab("/grid", [0, 0], [10, 5], [2, 1]);  // start, count, stride?, block?
f.free();

data is the typed array of the stored width (Float64Array, Float32Array also for f16, Int8Array ... BigInt64Array, BigUint64Array), or an array of strings for fixed- and variable-length strings and enumerations (h5py booleans read as "TRUE"/"FALSE"). Array datatypes are flattened, their dimensions appended to shape. Anything else throws an Error naming the type.

Limits

  • Read-only, and the whole file is held in memory (no range requests).
  • Compound, reference, opaque and variable-length-sequence datasets are refused with an error. Attributes of those types are listed with value: null and their dtype.
  • No Zstd or SZIP filters (they link C): such a dataset fails with unsupported filter. Deflate, shuffle, Fletcher-32, LZ4, N-Bit and scale-offset are read (within the limits in docs/known-issues.md).
  • Virtual datasets whose sources are in other files, and external links, cannot be followed: there is no file system.

Tests

test/run.sh builds the package, writes fixture.h5 (h5py) and fixture.nc (netCDF4) with test/make_fixture.py, then:

  • runs test/test.mjs under Node: every dataset (whole and a strided hyperslab), listing and attribute is compared with what libhdf5 reads back, error paths are checked, and so are the page's DOM-free helpers (viewer-lib.js);
  • runs test/browser.sh: loads the page in headless Chromium with ?file=fixture.h5&path=... for eight objects and checks the rendered tree, types, shapes, attribute and value cells, and the error shown for an unsupported type. Skipped when no Chromium is found (CHROME names one; a Playwright download under ~/.cache/ms-playwright is picked up). Drag-and-drop and the file picker are not driven by it; they share load() with the ?file= path.

The same expectations are checked natively, without Node, by crates/clawhdf5-wasm/tests/h5py_interop.rs, which is what CI runs (the CI container has no Node or browser).

Size

Measured 2026-09-26 on tank (rustc 1.98.1, wasm-bindgen 0.2.129, gzip 1.14, gzip -9 -n), after bash examples/wasm-viewer/build.sh:

raw gzip -9
pkg/clawhdf5_wasm_bg.wasm (profile wasm-release, opt-level s) 627,501 B 191,639 B
pkg/clawhdf5_wasm.js (wasm-bindgen glue) 21,826 B 4,487 B
same wasm at opt-level z 693,068 B 192,550 B
same wasm at opt-level 3 544,035 B 198,803 B
h5wasm 0.10.3: wasm embedded in dist/esm/hdf5_util.js 3,544,184 B 907,096 B
h5wasm 0.10.3: dist/esm/hdf5_util.js as shipped 4,150,134 B 986,699 B

h5wasm figures: npm pack [email protected] (npm reports dist.unpackedSize 14,731,385 B for the whole package), wasm extracted from the binaryDecode literal in hdf5_util.js. h5wasm is the whole of libhdf5 (writing, every datatype, plugins), so this compares download size, not equal functionality. No wasm-opt pass was applied (binaryen is not installed on tank). opt-level s is used because it is the smallest compressed.