rehype-matvis
v0.1.3
Published
Rehype plugin for rendering ML tensor/memory diagrams (shape/strides, matmul tiling, broadcasting) as inline SVG
Maintainers
Readme
rehype-matvis
A rehype plugin that turns fenced ```matvis code blocks into inline SVG diagrams for explaining ML internals: tensor shape/strides/byte-offsets, tiled matmul, and broadcasting. No external services, no rasterized images — just SVG spliced directly into your HTML at build time.
Install
npm install rehype-matvisUsage
Drop rehypeMatvis into your existing unified/rehype pipeline, immediately after remark-rehype and before any syntax highlighter (e.g. rehype-shiki, rehype-pretty-code). If it runs after the highlighter, the highlighter will try (and fail) to tokenize the matvis language block.
import rehypeMatvis from "rehype-matvis";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
const processor = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeMatvis) // <- here, before any syntax highlighter
.use(rehypeStringify);rehype-matvis splices real hast element nodes into the tree (not a raw node), so it works as a drop-in step — you do not need rehype-raw anywhere in your pipeline. The ```matvis code-fence language tag stays matvis regardless of the package name.
Then in a markdown file:
```matvis
type: strides
shape: [4, 4]
strides: [4, 1]
```DSL grammar
Every diagram is a YAML mapping with a required type discriminator (strides, matmul, or broadcast) plus type-specific fields. Shared optional fields: title, dtype, width.
type: strides — shape / strides / byte-offset grid
type: strides
title: "Row-major 4x4 float32 matrix"
dtype: float32 # affects byte-offset math; default 4 bytes if omitted/unknown
shape: [4, 4] # 1 or 2 dimensions
strides: [4, 1] # in elements, not bytes
highlight:
- cells: [[0, 0], [0, 1]]
color: accent # accent | warn | info | muted
label: "stride[1] = 1 elem"
byte_offsets: true # show computed byte offset per cell
base_offset: 67 # optional: byte offset of element (0,0) in the enclosing buffer; added to every cell (default 0)
order: row-major # row-major | col-major (informational, for the caption)type: matmul — tiled matmul (A[M,K] @ B[K,N] = C[M,N])
type: matmul
title: "C = A @ B, tiled 2x2"
dims: { M: 8, K: 4, N: 8 }
tile: { M: 2, K: 4, N: 2 } # must evenly divide dims
highlight_tile: [0, 0] # which output tile to spotlight, and its contributing A/B strips
labels: { A: "A [M,K]", B: "B [K,N]", C: "C [M,N]" }type: broadcast — elementwise broadcasting
type: broadcast
title: "Broadcasting (3,1) against (1,4)"
operands:
- shape: [3, 1]
label: "A"
- shape: [1, 4]
label: "B"
result_label: "A + B -> (3,4)"The result shape and which axes stretch are computed automatically — you only describe the operands.
Theming
Every generated SVG ships an inline <style> block that reads CSS custom properties, with light-mode fallback colors baked in:
| Variable | Purpose | Default |
|---|---|---|
| --matvis-bg | cell fill | transparent (page background shows through) |
| --matvis-fg | text/stroke (also falls back to currentColor) | currentColor |
| --matvis-border | cell border | currentColor |
| --matvis-accent | highlight color 1 | #6366f1 |
| --matvis-warn | highlight color 2 | #f59e0b |
| --matvis-info | highlight color 3 | #0ea5e9 |
| --matvis-muted | highlight color 4 / brackets | #9ca3af |
To support dark mode, define these variables once under your site's dark-mode selector (e.g. [data-theme="dark"]) — every diagram re-themes automatically, no JS required.
Known limitations (v1)
stridesandbroadcastoperands support at most 2 dimensions.- Text that doesn't fit a cell shrinks its font size once; it does not wrap or relocate outside the grid.
- Highlight groups that share a corner cell (e.g. showing both a row-stride and a column-stride bracket from the same origin cell) can crowd their labels — pick non-overlapping highlight groups where legibility matters.
- Layout is deterministic grid arithmetic (no collision-avoidance engine), matching a diagram-authoring tool rather than a general graph layout tool.
Development
npm install
npm test # vitest: DSL parsing, IR/snapshot, and full-pipeline integration tests
npm run build # tsup -> dist/
npm run preview # renders preview/posts/*.md through the same pipeline shape as a real blog, to preview/dist/*.htmlpreview/dist/index.html links to one rendered page per diagram type, with a light/dark theme toggle button, for visual QA without needing a real blog.
