Vizzu makes the chart type a channel binding
Library for animated data visualizations and data stories.
At a glance
- What is it?
- Vizzu is a JavaScript and C++ charting library where the chart type falls out of which series you bind to x, y and color, and every change of state is animated by a WebAssembly core. The npm package ships one minified script next to a cvizzu.wasm binary, and the four examples never show a way to render a state without animating it.
- Who is it for?
- Vizzu fits teams whose deliverable is a sequence of views over one dataset, where the transitions are the point and each state is a handful of channel keys. It does not fit a single static chart, a printed figure, or a project with an accessibility requirement the documentation must answer first.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A chart is a set of channel bindings, not a chart type
The dataset in the README is three plain objects, each with a name and a values array. One holds strings, two hold numbers:
let data = {
series: [{
name: 'Foo',
values: ['Alice', 'Bob', 'Ted']
}, {
name: 'Bar',
values: [15, 32, 12]
}, {
name: 'Baz',
values: [5, 3, 2]
}]
};Binding the string series Foo to the x channel and the numeric series Bar to y yields a bar chart, because one channel carries categories and the other carries measures. The chart type is never named. There is no `type` key anywhere in the examples and no call that says which chart to draw, so the shape of the values decides the role of each series and the constructor receives only a container id and the data:
let chart = new Vizzu('myVizzu', {
data
});That is the entire configuration surface shown, and it is the most interesting thing about the API. Libraries organised around a chart type constant ask you to pick a family and then describe it; here you describe coordinates and let the engine classify them. The trade is that no configuration line states its own intent, since the series names are the only strings connecting a channel to a column, and the examples never show what happens when a channel names a series that is not in the data.
animate is the only mutating call the examples show
State changes go through one method, and it takes the same object shape as the constructor:
chart.animate({
x: 'Foo',
y: 'Bar'
});Then the same chart becomes a scatter plot with three keys and no mention of y:
chart.animate({
color: 'Foo',
x: 'Baz',
geometry: 'circle'
});`geometry: 'circle'` swaps the mark for a circle, `color: 'Foo'` reuses the string series as a dimension, and `x: 'Baz'` moves the other measure onto the horizontal axis. What the pair of snippets leaves open is the fate of a channel you stop mentioning. `y: 'Bar'` is set in the first call and simply absent from the second, the examples never show a `y` key being emptied or replaced, and so whether the old binding persists or is cleared is not something these four blocks answer.
More consequential for anyone building a data story is what surrounds those calls. There is no promise to await, no completion callback, no animation-finished event and no example of two states changing one after the other, so the shown API cannot express run this transition and then that one. Sequencing has to come from the reference documentation, and the README hands off to it before answering either question.
The C++ core ships as a WebAssembly binary beside one script
Three bullets in the feature list set the architecture: HTML5 canvas rendering, written in C++ compiled to WebAssembly, and dependency-free. package.json points `main` at `dist/vizzu.min.js` and `types` at `dist/index.d.ts`, declares `"type": "module"`, and adds subpath exports for the binary and for JSON:
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/vizzu.min.js",
"default": "./dist/vizzu.min.js"
},
"./*.wasm": "./dist/*.wasm",
"./*.json": "./*.json"
},The `files` array publishes `dist/cvizzu.wasm` alongside the minified script and the `.d.ts` declarations, so the animation logic is not inside the JavaScript a browser parses. A bundler that honours the subpath export reaches the binary through the package, while the top level of the repository lists no `dist/` directory at all, which means both paths are build output rather than committed source.
Keeping the geometry engine out of the main bundle is what lets the project claim dependency-free for consumers. It also moves part of the runtime cost into a binary fetch at load time, and the README does not discuss that, beyond an install size badge on npm.
Aggregation and filtering are claimed in a bullet, not in code
Automatic data aggregation and data filtering sit in the main feature list, and none of the four example blocks pass an aggregation or a filter option. The first `chart.animate` call carries two channel keys and nothing else, and the second adds a colour binding and a geometry key. Whatever the engine does when many rows collapse into fewer buckets, or when a filter narrows the data, happens behind configuration the examples never show.
The same gap sits under defaults based on data visualization guidelines. A guideline default is useful right up to the moment you want the other default. There is no documented key in these snippets for tick count, label rotation, legend position or axis order, so overriding any of them is not something the README lets you do. For a dashboard whose house style is fixed by a design system, that distinction matters, and the answer sits in the reference documentation rather than in the code you copy.
Canvas rendering means the host page owns the size
Sizing lives in the markup rather than in the chart configuration:
<div id="myVizzu" style="width:800px; height:480px;">
</div>That 800 by 480 box is CSS on a placeholder element, and the constructor takes only `myVizzu` and the data, so the chart fills whatever space the page gives it. It is a small thing to read and an easy one to miss, because the chart is built from an id and a data object with no dimensions anywhere in the call.
Canvas rendering has a second consequence that matters more. A canvas holds no per-mark DOM node, so hover targets, focus order and text alternatives cannot be attached to an individual bar through the element tree, and the README documents nothing about keyboard navigation, screen reader output or a data table fallback. An accessibility requirement sends you to the reference documentation, because the WebAssembly core will not hand you a DOM tree to traverse.
The CDN example loads an unpinned @latest build
The script tag in the installation section points at a moving target:
<script type="module">
import Vizzu from 'https://cdn.jsdelivr.net/npm/vizzu@latest/dist/vizzu.min.js';
</script>`@latest` resolves to whatever the publisher uploaded most recently, so a page that rendered on Tuesday can draw differently on Wednesday with no change in your code and no version bump in your package.json. For a library whose argument is that viewers can follow a transition from one chart shape to another, reproducibility is the point, and the demonstration pins nothing. Put a tag such as `v0.18.0` in that URL on any page that has to keep producing the same picture.
The rest of that snippet is copy and paste shape rather than a documented loading contract. The module import sits in the head of the page while the element it draws into appears later in the body, and the usage example imports Vizzu a second time at the top of its own module. None of that breaks anything, but the snippet never states an ordering requirement, so a reader has to work out for themselves whether the import may live in the head at all.
Contributors carry Node, Python and two lock scripts
The root of the repository is where the build gives itself away: `.clang-format` for the C++ sources, `eslint.config.mjs`, `.prettierignore` and `package-lock.json` for the JavaScript, a `.puppeteerrc.cjs` for browser-driven checks, and directories for `src/`, `test/`, `docs/`, `project/` and `tools/`. The `files` array keeps all of that out of the published tarball, which carries `dist/cvizzu.wasm`, `dist/vizzu.min.js` and the type declarations, and it lists `LICENSE` and `NOTICE` alongside them because package.json declares the licence as Apache-2.0.
The scripts tell a different story from that summary. `lock:js` runs `npm update` while `lock:py` runs `./tools/ci/run/lock-py.sh`, `format-src:js` runs `npx prettier -c src test package.json`, and `format-docs:py` runs `./tools/ci/run/format-mdformat-py.sh --check docs README.md CONTRIBUTING.md CODE_OF_CONDUCT.md FAQ`. A contributor therefore needs Node and Python on the machine plus `npm-run-all` to drive both, even though a consumer of the package needs nothing at all.
Every published tag is still inside the 0.x series: v0.17.0 on 2025-07-31, v0.17.1 on 2025-08-24 and v0.18.0 on 2026-04-20, a gap of eight months between the last two, and the last push carries the same date as v0.18.0. The project has not stalled, but neither has it moved quickly, and no tag has reached the first stable version where breaking changes would land on somebody else's schedule.
What changes when the animation is not your job
The usual shape of this problem is a renderer plus a transition layer you write yourself: pick a chart type, hand over a configuration, then add keyframes or CSS for every pair of states you want to move between. Vizzu relocates that work into the C++ core, so from the caller's side the engine takes the previous state, the new state and the data, and the browser receives one canvas whose pixels change over time, with the mark chosen by the `geometry` key and the colour split chosen by the `color` key.
What you give up is equally concrete. Every change in the README travels through `chart.animate`, and no example sets a state without animating, so a chart that has to exist at a fixed size in a PDF, a printed report or a screenshot has no documented static path. Not one of the four snippets renders a final frame and stops.
There is a bundle-level cost as well, since `dist/cvizzu.wasm` ships whether or not you ever animate: the same binary draws the very first chart too. For a team publishing one chart, that trade is bad. For a team shipping a scrollytelling piece where one table is re-expressed a dozen times, the state objects stay small and the animation is the product, so it reverses.
Editorial conclusion
Vizzu fits teams whose deliverable is a sequence of views over one dataset, where the transitions are the point and each state is a handful of channel keys. It does not fit a single static chart, a printed figure, or a project with an accessibility requirement the documentation must answer first. Check whether the CDN usage in your pages can pin v0.18.0 instead of @latest, read the reference for the aggregation and filter keys these examples leave out, and decide whether a canvas with no static render path fits the way you ship.
Frequently asked questions
How do I install Vizzu without a build step?
Run `npm install vizzu`, or import the module build straight from a CDN. The README's script tag loads https://cdn.jsdelivr.net/npm/vizzu@latest/dist/vizzu.min.js inside a module script block in the page head.
What changes between a Vizzu bar chart and a Vizzu scatter plot?
Only the channel bindings. A string series on the x channel with a numeric series on y gives bars, while a numeric series on x, the string series on `color` and `geometry: 'circle'` gives a scatter plot. No chart type key appears in either call.
Does Vizzu need npm dependencies to run?
No. The feature list calls it dependency-free, and the package `files` array publishes `package.json`, `README.md`, the WebAssembly binary, the minified bundle and the type declarations, with no runtime package listed alongside them.
How do I chain several Vizzu animations in a data story?
The README does not document it. Its examples show `chart.animate` calls but no promise, callback or animation-finished event, so sequencing one transition after another has to be resolved in the code reference.
Can Vizzu produce an accessible chart with a data table?
Nothing in the README addresses accessibility. Charts draw to an HTML5 canvas, so the element tree carries no per-mark node, and the project points readers at its own FAQ page for such questions.
Why does Vizzu ship a separate .wasm file?
The chart engine is C++ compiled to WebAssembly, and package.json resolves `./*.wasm` subpath exports to files in `dist`. The `files` array publishes `dist/cvizzu.wasm` next to `dist/vizzu.min.js`, so a bundler that ignores the exports map has no documented way to locate the binary.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vizzuhq-vizzu-lib)Was this useful?