vccs
Composable charting components for Vue 3, ported from Recharts
Screen and compare FDM filaments for Bambu Lab H2C printers
AI summary · website and README
DataJavaScript · CSSGitHub @faridbnvOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Loading the full README. The saved excerpt is shown below.
A self-contained browser application for choosing FDM filaments for a fully configured Bambu Lab
H2C. It compiles a frozen research database into a single HTML file that runs offline, from a local
file, a shared drive or static hosting, with no backend.
The tool is decision support: screening, comparison and evidence navigation. It is not a source
of certified design allowables, not a substitute for reading the exact grade's technical and safety
data sheets, and not a guarantee that any third-party filament runs on an H2C.Read the full README ↗Live: pdynamics.ca/h2c-materials · one HTML page, with a page of its own for each well-documented material beside it, that also runs offline from a file, with no server.
Filament Atlas is a tool for a small engineering team choosing a 3D-printing filament for the team's Bambu Lab H2C printer. It was the H2C Material Selector until 2026-10-16; the built file keeps that name. You say what a part must do (survive 100 °C, be at least this stiff, be as light as possible), and it answers in two steps:
Every number comes from a maker's data sheet or web page and can be traced back to its page. Where nothing is published, the tool says so instead of guessing; a marked estimate is shown only where no product publishes a value. What nothing settles yet is sorted by what would settle it: one check away, not enough data, or unlikely to meet, each naming the product that decides it (D155). It helps the team build a shortlist; the team confirms its final pick with its own test print. It is not a source of design allowables, and it does not replace the product's own data sheet.
Exact, current counts are in build/snapshot/counts.md, written by npm run snapshot.
What is known to be wrong or missing, item by item and with the query that re-counts each, is docs/OPEN-PROBLEMS.md. What the tool is for is in docs/GOALS.md, and every decision the owner has made, dated, in docs/GOALS-LOG.md.
data/tables/). It changes only through small scripts that name the value they
replace and check it against the source page, and a set of automatic checks must pass before anything is published
(AGENTS.md).Needs Node 24 or later, and Chrome (or Chromium) for the interface checks in npm run verify.
docs/OPERATING.md is the short guide for a person: these commands, and the three common jobs step
by step (correct a value, add a data sheet, publish).
npm ci --prefix build && npm ci # once: the build's dependencies, and the import tools'
npm run doctor # what this checkout can run, and what each missing piece needs
npm run hooks # once per clone: the pre-commit data check
npm run verify:fast # while you work: format, schema, lint, generated docs, build and tests (about a minute)
npm run verify # before a commit: every check, the page in a browser included (about seven minutes)
npm run build # -> dist/H2C_Material_Selector_<snapshot>_<release>.html, dist/materials/ (the material pages), dist/manifest.json
npm run build:diff # what a change did to the compiled database, against HEAD
npm run trace -- PETG # any value back to its measurement, product and source page
The other commands (data:*, ingest:*, audit:*, sql, packets, round:close) are listed in
package.json; AGENTS.md and docs/ARCHITECTURE.md say when each is used.
Changing data? Read AGENTS.md first. A new data sheet enters only through the import pipeline (docs/IMPORTING.md), open since 2026-10-05, when the owner lifted the pause of 2026-09-25; docs/WALKTHROUGH-ADD-A-MATERIAL.md shows, as history, how a material's records fit together. docs/README.md names the three routes: choosing, correcting a value, importing.
What is known to be wrong: docs/OPEN-PROBLEMS.md. The database records its own defects rather than hiding them, so that page is where they are listed, each with the query that re-derives its count: the published values physics rules out, the source conflicts nobody has been able to settle, the conditions the reader could not pair, and the materials nothing has been published about.
Open the file in dist/ in any current browser. Nothing else is required; its drawer's Full page links and the status
bar's Material pages link open the pages in dist/materials/ beside it, and their Back to your results returns to it.
.github/workflows/verify.yml runs verify's data checks, interface views and rendered scenarios (in three parts), side
by side, on every push to main and on pull requests; when all pass, .github/workflows/pages.yml rebuilds the page and the material pages from data/tables at that
commit and publishes them to GitHub Pages. A branch is checked locally (npm run verify, every step) before it is merged; the scale
and reproducible-build checks and 2,000 random scenarios run weekly. The
distributable HTML is not committed, so the published page cannot drift from the source of truth, and a database
that fails validation stops in CI and never reaches the site.
The page is served at the site root, so the address opens straight into the tool. The release-stamped filename and the validation report are published alongside it:
| Address | What |
|---|---|
/h2c-materials/ | The tool, at its current release |
/h2c-materials/H2C_Material_Selector_<snapshot>_<release>.html | The same page under its own name: its data date, and its release |
/h2c-materials/validation-report.md | What the compiled database cannot support |
/h2c-materials/manifest.json | The release, the digests it is made of, the commit, the runtime, input hashes and output hashes the page was built from |
/h2c-materials/materials/index.html | Every material with a page, by family |
/h2c-materials/materials/<MaterialID>.html | One material's Overview; <MaterialID>-products.html its products, each with every record behind it (-records.html redirects there) |
/h2c-materials/materials/method.html | How the numbers on the material pages are worked out |
A release is the content that decides every answer: the tables, the schema, the build's rules, the selection
engine and the templates, and the locked dependencies (D96). Its twelve-character ID is a digest of them, so the same
inputs give the same ID on any day, and a change to any of them gives another even on the same data date (the date in
method.csv is for a reader, and stayed one date while the data changed). The page shows its release at the right end of its
status bar and under Save / share; every saved scenario, link, CSV export and decision brief carries it, and a scenario reopened on
another release says so before it asks its question again.
Every release published from main is kept for good as a GitHub release named h2c-<release>, with its page, its
manifest and its material pages (H2C_Material_Pages_<snapshot>_<release>.zip, to unzip in the folder that holds the
page) attached: download the page and open it from disk to see the answers a scenario was saved with, offline. The
site itself always serves the current release.
To check a deployment, wait for the latest main Publish to Pages workflow to succeed, then read the live
manifest.json. Its commit must match the deployed main
commit, and its release must match the one in the page's status bar and the corresponding h2c-<release> GitHub release.
The data snapshot date can remain unchanged across releases; it does not identify the latest version.
The documentation and private source cache are not part of the public site: maintained docs live in this repository,
and original manufacturer documents stay in the private source store.
The 2026-09-28 version 2.1 and targeted gap-closure publication is kept as release ba30857bd40d, with its HTML and manifest.
| Read this | For |
|---|---|
| docs/OPERATING.md | The short guide for a person: the five commands, and how to correct a value, add a data sheet and publish |
| docs/GOALS.md, docs/GOALS-LOG.md | What the tool is for and how a change is judged; the owner's decisions, dated |
| docs/ARCHITECTURE.md | The three layers, the module map, where to add things, and what each build stage does and refuses to do |
| AGENTS.md | How to change data, for people and AI agents alike |
| docs/HOW-IT-WORKS.md | For an engineer using the tool: where a number comes from, what each kind means, and how far to trust it |
| docs/DATA-MODEL.md | The tables, the registry, the compiled shape, the three kinds of number |
| docs/INTERFACE.md | The workflow, the lenses, the words, the visual vocabulary |
| docs/DECISIONS.md | The non-obvious decisions, and the bugs that forced them (DECISIONS-ARCHIVE.md holds the few no longer in force) |
data/tables/ the source of truth: one CSV per table, canonical form
data/tables/properties.csv the property registry; headline_definitions.csv the headlines
data/tables/polymers.csv what the estimate model knows about each polymer, with provenance
data/tables/polymer_environment.csv a base polymer's published behaviour in chemicals and exposure; shown, may screen, never passes (D64)
data/manifest.json row count and SHA-256 of every table
data/review/accepted-findings.csv every lint or build finding a reviewer accepted, with the reason
schema/tables/ schema/vocab/ the declared contract for every table, and its vocabularies
schema/db.schema.json the contract for the compiled database
schema/estimate-model.schema.json the contract for the estimate model's configuration
build/src/pipeline.js the stages every caller runs: compile, the estimate stage, validate
build/src/ check -> load -> normalize -> compile -> estimates -> validate -> contract -> bundle,
then the material pages (stage 8, app/pages/)
build/src/estimate/ the estimate stage, an overlay on the compiled database (D58)
build/src/coverage-rules.js one definition of what counts as a material's own data
build/mappings/estimate-model.json the estimate model's physical limits and fitting judgements, reviewed like code (its conversions: data/tables/conversions.csv)
build/snapshot/ the committed review snapshot: counts, headlines, products and spreads, each
product's states, print gates, environment, makers' claims, templates, warnings,
screening ends, the material pages, interface views
build/reports/ the validation report, regenerated every build
app/js/engine/ the selection logic. Pure: no DOM, never imports from ui/
app/js/ui/ rendering and interaction
app/js/ui/registry.js labels, filters, axes, columns and export, built from the registry
app/js/ui/labels.js the one vocabulary: what every criterion and verdict is called
app/js/main.js the only place that holds state
app/pages/ the material pages' renderer, the build's stage 8 (D142)
test/ engine, data gate, registry, contract, database and import (ingest-*) tests;
the scale, reproducible-build and material pages' checks (*.check.js), which verify runs
scripts/data/ fmt, check, lint and build-finding review, new, new-material, retire, new-id, diff, the edit API, review workbook, scale data, the SQLite file, the source store, packets (a round's working files to the private store)
scripts/lib/ shared helpers: guarded source edits, proposals applied quote by quote, checks on a cached sheet's text, headless Chrome
scripts/audit/ source completeness (every PDF source re-read for values not in the tables),
each value's context and print setting against its cached sheet (audit:context),
where a print-profile error could hide on a cached sheet (audit:profile-marks),
blocking gaps, the know-how worklist, the spot-check and final-round samples
scripts/build-diff.mjs what a change did to the compiled database, against HEAD
scripts/snapshot.mjs, ui-probe.mjs the committed review snapshot and interface views (build/snapshot/)
scripts/ui-fuzz.mjs, lib/cdp.mjs random scenarios through the built page, checked against the engine; shared headless Chrome
scripts/trace.mjs a headline back to its source
scripts/pages/ the material pages' tools: render (design work), fields, page text, add-summaries, subset-fonts
scripts/audit-data.mjs record/family inventory and source-to-HTML checks
scripts/docs-*.mjs the generated documents and their checks: rules, data dictionary, decision index, README status, history marks, Markdown links, open-problem counts
scripts/round-close.mjs what a closing round leaves open; moves its migrations to archive/migrations/
scripts/migrate/ an open round's source corrections and table changes, each guarded; its README is the template
archive/migrations/ every earlier migration (m10 onwards), each run once and kept as the record; round:close moves them here
archive/workbook-conversion/ the 2026-09-14 workbook conversion (m01-m09, ledger, replay, baseline); history, no longer runs
archive/ingest-2026-09-18/ the V2 import's price captures, and the record of its proposals (in the private store since 2026-10-15)
archive/research-2026-09-26/ what became of each finding of the 2026-09-26 research package, and its questions for makers
.githooks/pre-commit format, schema and no-deletion check on data commits
.github/workflows/ verify on every push to main and pull request, a weekly trend run; publish to Pages once verify passes on main
dist/ build output, not committed
docs/ how it works and why
docs/background/ the research inputs it was built from
docs/audits/<date>-<subject>/ one folder per audit: the report as delivered, what was done, and PACKETS.md naming its working files in the private store
These come from the database's own Method table, the architecture brief and the audits. They are not stylistic preferences: changing one changes what the tool asserts.
headlines.csv
only pins a product's value where the rule chooses wrongly, and a bad pin fails the build.Only the first is evidence. The interface renders them differently on purpose.
| What it is | Looks like | |
|---|---|---|
| Measured | Its products' comparable values: their typical value (the median), and under it their range and how many products, each product's value traceable to one measurement, grade and source | 2.3 over 1.0–3.0 · 27 |
| Related | A real measurement of the same property that is not a comparable value of any product. It decides a requirement only through a conversion row of the evidence ledger, in that row's one safe direction, and the answer names the row (D153) | 46* |
| Estimated | The likely (80%) range of a calibrated model of every observation. Inference, not evidence | ~71.3–92.5† |
Estimates exist because in Explore a material with no mechanical data answered UNKNOWN to every mechanical criterion, so PLA Lite sat among the elastomers in a search for "elongation at least 100%". The first estimate model fixed that by pooling whole families and treating a sample's extremes as a bound, which was wrong in its own way; the second stopped estimates deciding anything; the third used only like-for-like evidence and gave ranges too wide to use, such as PA-CF strength 38–204 MPa. The current model fits one calibrated Gaussian model per property to every observation in the database, each converted to the headline (a break strength, a flexural modulus, a Z value, a resin data sheet), with polymer, reinforcement and melting-point structure shared across a family. PA66-CF sits above PA66, and hidden measured values fall inside the shown 80% range about 80% of the time (the build fails if that drifts; the figure for each property is in the validation report). Every in-scope material that names an Estimate identity has, for each estimated property (density, stiffness, strength, stretch, heat deflection), a value, an estimate or a reason it does not apply; the other properties show Not published where nothing is. An estimate never passes a material: it orders the materials under "Not enough data" and tags "likely" the ones whose estimated range meets every open limit (D155). A material with products is judged on each product's own values, so its material estimate is context only; a material with no product on record is hidden by its estimate only when its screening range wholly fails (at least the 95% range, wider where the back-test shows it must be, D59) and the material's own data does not contradict it (D148). The switch for all of this is "Use estimates and resin data". See the data model and DECISIONS D43.
Strict and Explore are the names used in the code and in these documents. On screen the control is labelled "Materials that could not be checked", and the two buttons read "Leave out" and "Include" ("Confirmed only" and "Include uncertain" until 2026-10-18), because the words Strict and Explore told a first-time reader nothing about what they did.
350 °C nozzle, 120 °C bed, 65 °C active chamber, from the Method table. The build parses each
profile's published requirement and compares it against this envelope. The materials marked out of scope (Scope
Excluded; how many is in build/snapshot/counts.md) are the industrial high-temperature
polymers. Most of those that publish a nozzle temperature ask more than the H2C's 350 °C on one product or more
(PEEK, PPSU, PEI, TPI, and PEEK-CF, PEKK-CF, PEI-CF and PEI-ESD); PEKK, PSU and PESU publish windows the H2C reaches
only at their low end or at its edge (D151), and the others publish no print setting. Scope, not one window, keeps them
out: their typical processing needs more than the H2C gives. The metal and ceramic sintering filaments, never
candidates whatever they print at since the part is the sintered metal's or ceramic's (D87), left the dataset on 2026-10-20 (Scope Retired, D174): their records stay in the
tables, and the selector, the pages and the exports hold none of them.
A window that starts inside the envelope and ends above it is read by how much of it the H2C covers (D151): Bambu
PPS-CF's 60–90 °C chamber is within, at its low end; LCP's 120–150 °C bed, which the H2C reaches only at its edge, is on
the line.
build/reports/validation-report.md is a deliverable, not console noise. It records what the
compiled database cannot support, so the interface can say so rather than implying a certainty it
does not have. Its warnings name their records: outlying headlines, imprecise estimates, family-order breaks and
materials without measurements. Each record is fixed or accepted with a reason in
data/review/accepted-findings.csv, and verify fails on one that is neither (D57).
Product uncertainty (D137, D155): under Include (materials that could not be checked) the list is sorted by what would settle each material: "Meets every requirement", "One check away" (one question to the maker or one test print settles it: its print settings, or a value on the line), "Not enough data" (the likely ones first) and a folded "Unlikely to meet" (a product measured the way asked misses and none meets it, or every open product's own estimate misses). Each row names the product that decides its group, with its own numbers. Folded rows stay in counts and exports, and no group changes a verdict. Missing product values use their own estimates regardless of sibling coverage; predictions never pass. Independent exclusion permission is currently limited to validated as-printed/dry contexts under the reserved complete-scenario risk budget; no class currently meets that gate. Unsupported scopes remain unresolved. What hides a material under Include today is a property that does not apply to it, with "Use estimates and resin data" on or off, and, with it on, resin data reporting its polymer attacked or the estimate of a material with no product (D148). The fix audit records the risk gates and decision differences.
| docs/IMPORTING.md | How a new data sheet enters, through the import pipeline |
| docs/audits/ | Every audit of the tool and the database, each with its report and what was done about it; a round's working files are in the private store, listed in its PACKETS.md |
| docs/background/ | The research inputs this was built from |
app/js/ui/labels.js,
so no screen can print an internal key while the screen beside it reads plainly.Gap beside the
material's own data or claim evidence that belongs only to another material. Family citations
may remain as context in use, durability and safety notes; they are not grade evidence.