vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Barnes maze video analysis in the browser: classical CV tracking, hole investigations and escape entries, latency/error/path/strategy metrics, hand corrections that propagate, and CSV/XLSX export stamped with every parameter and the tool version. No install, no GPU, no upload.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Turn a folder of Barnes maze videos into latencies, errors, path measures and search-strategy
calls that you can defend six months later — in a browser tab, with nothing to install.
Live:
Demo video:
Nothing to hand? Press Load example cohort on the Videos step to open a worked cohort with
every result already computed.
What it does, and who it is forREADME 전문 보기 ↗Turn a folder of Barnes maze videos into latencies, errors, path measures and search-strategy calls that you can defend six months later — in a browser tab, with nothing to install.
Live: https://barnestrack.pages.dev Demo video: https://youtu.be/1WjdhPpNm9Q Nothing to hand? Press Load example cohort on the Videos step to open a worked cohort with every result already computed.
BarnesTrack is for the person who runs the maze: a student or postdoc with sixty videos, a deadline, and no wish to become a software operator. It does not need a terminal, an install, an account, a GPU or admin rights, because it is a static page that runs entirely in the browser already on the laptop.
The work is four steps, and the page says the same thing at the top of each one:
Three things shape everything else:
Every threshold that decides a number is a named parameter rather than a constant buried in code: adjustable in the interface wherever the step that uses it has shipped, and carried into every export either as its own column or through the parameter set the export's hash covers, alongside the tool version. Two spreadsheets that disagree can be reconciled from what travels with them.
Open https://barnestrack.pages.dev and drop in your videos. There is no sign-in, no upload and no setup.
Supported browsers: Chrome or Edge 94 and later, Safari 16.4 and later, or Firefox 130 and
later. The page checks at load and says so in plain language if the browser cannot decode video
frames; it does not fall back to a lower-fidelity path, because every measurement is tied to a
specific frame of the file (see D7 and D4 in docs/decisions.md). An old
laptop is fine — Chrome decodes H.264 in software and the tracker runs on the CPU at over a
thousand frames per second. Input is MP4 with H.264 video; anything else is listed with the reason
and an ffmpeg line that converts it, never silently ignored.
Node 24 or later (the version is in .nvmrc). No environment variables, no API keys, no
services to provision.
npm ci # dependencies are pinned to exact versions
npm run dev # http://localhost:5173
npm test # unit tests (vitest)
npm run lint
npm run typecheck
npm run build # static site in dist/, deployable as-is
npm run test:browser:ci # browser checks CI runs, in your installed Chrome; needs dist/ and ffmpeg
npm test covers the pure layers — metrics, event detection, cleaning, strategy, maze geometry,
the export writers, the session file and the MP4 parser — and the DOM-free parts of the interface.
Tests that need the sample videos or the demo take's session file skip themselves and say so.
Three optional extras:
BARNESTRACK_SAMPLE_DIR=/path/to/data/barnes-maze npm test includes the tests that read the
sample videos. Without it those tests skip themselves and say so. No video is committed to this
repository; the clips live in the sample-data repository.BARNESTRACK_DEMO_SESSION=/path/to/session.barnestrack.json npm test includes the two tests that
regenerate the bundled example cohort from the demo take's saved session
(scripts/build-example-bundle.ts). That file is 11 MB and is not committed either.npm run test:browser runs every browser check against your installed Google Chrome (set
BARNESTRACK_SAMPLE_DIR to include the sample-video specs). CI runs two of them, the shipped-UI
flow and a generated clip through tracking; tests/browser/README.md
says what each spec covers and records what was verified by hand instead.examples/outputs/ holds the CSVs and parameters.json this tool exported
during the recorded demo, unmodified — what the deliverable actually looks like, without running
anything.
CI runs lint, typecheck, tests and the build on every push and pull request, and
npm run test:browser:ci in a separate job (.github/workflows).
One consequence worth stating plainly: the deployment configuration is not in this repository. The
site is built and served by Cloudflare Pages from the dashboard — build command npm run build,
output dist, no environment variables — so a fresh clone gives you the application but not the
hosting. The settings are written down in D43 of docs/decisions.md so they
can be reproduced from the record rather than from memory.
The full record, with the reasoning for each, is in docs/decisions.md; the
file formats and internal representations are in
docs/data-contracts.md. Dnn and Onn cite entries in
docs/decisions.md; the O entries were open questions, all closed on 2026-09-07. The themes:
A page, not a service (D2, D3, D41). BarnesTrack is a static client-side build with no server,
no accounts, no API keys and no runtime network requests at all — no CDN script, no web font, no
analytics, an inline SVG logo. That makes "what leaves the user's machine?" answerable as nothing
and checkable in the browser's own network tab, and it means the tool still works on a lab laptop
with no internet. There is no UI framework: the correction surface is a canvas, where a framework
adds little, and TypeScript turns the data contracts into types the compiler checks. mp4box.js
and exceljs are the only runtime dependencies, both pinned exactly.
Classical computer vision, chosen rather than settled for (D6). A per-pixel temporal-median background, an Otsu threshold with a live preview, connected components with an area prior and proximity to the previous position, a morphological opening to strip the tail, and the nose taken from the body ellipse's major axis with the head end chosen by tail direction, motion and hole proximity. Every step is explainable on screen and runs at over a thousand frames per second on a CPU. Segmentation models and pose estimators are legitimate tools, but browser-side inference at 0.2–3 s per frame cannot process three videos while someone watches, a mask still leaves the nose to be inferred from shape, and the named-point contract admits an imported model track later anyway. D6 also writes down the fallback ladder if the approach fails on a video — tighter mask, per-video threshold, adaptive threshold, an in-browser WASM segmenter — and rules out a server at every rung.
Contracts before features (D7–D12). A frame is its position in the MP4 sample table, and its
time comes from that table rather than from frame ÷ fps. Every position is a named point with
confidence, a valid flag and a source. A session is one JSON document with three layers per video:
auto, which a tracking run writes once and nothing ever mutates; corrections, a sparse list of
human edits; and derived, recomputed from auto ⊕ corrections on load. The maze map is
parametric, shared across the cohort, and placed per video. Exports are three tidy CSVs plus
parameters.json, the session file and an XLSX, every row stamped with the tool version, the
schema version and a parameters hash — and every trial row with its target_hole, because Gawel's
protocol rotates the platform between trials and a latency is meaningless without the hole it was
measured to (D62), which is why a video can name its own target and its trial type, acquisition or
probe, a probe having no escape box and so no escape measures (D68). The session carries a stable
id and a reviewer, stamped onto every correction, so a merged spreadsheet joins on the id and an
ok that rests on a person's confirmation says whose (D68). Whether an event is the tool's or a
person's is decided by running the detection twice, over the automatic track alone and over the
corrected one, and comparing: an event only the corrected run finds is the person's, one both
find but differently is the tool's with corrected evidence, and the automatic values travel beside
it (D65). Where there is no trial window there is no number — the cell is blank, never zero, and
the strategy reads unclassified, never a confident class of nothing (D66). An escape entry is an
inference a person can confirm, reject or reclassify as a loss of tracking (D67). Two places where
the sample data forced a refinement are
worth naming: same-timestamp frames are ordered by the bitstream's picture order count, because
the decoder emits them that way and a simpler rule makes the tracking pass non-monotone (D45); and
an animal split into pieces by a hole's shadow is merged into one candidate, marked
low_confidence / fragmented rather than reported as ambiguous, because splits happen exactly
where investigations are detected (D48).
Honest failure, and thresholds you can see (D16–D20). The automatic layer never interpolates: every frame without a position carries a state and a reason string, and the quality report clusters on those reasons. Gap filling is off by default (D60): a gap stays a gap, is drawn as one, and enters no kinematic value. A lab that wants it can turn it on — the filling is then confined to the derived layer and to gaps within a documented limit, and the filled points are drawn hollow and counted — but no data is better than invented data. Investigations, escape-box entries and tracking failures are told apart by evidence the user can inspect — where the animal was last seen, how long detection was lost, whether it came back and where, and the blob-area trend before the loss — and each event seeks to its own first frame on click. Both the nose and the centroid are always tracked; events use the nose when its heading confidence clears a visible cutoff and the centroid otherwise, recording which per event, because a confident wrong nose is worse than an honest centroid. Changing a parameter re-renders the events and the metrics immediately, with a badge showing what changed.
A strategy call you can read aloud (D23, D58). Search strategy comes from a transparent rule engine over named features — errors, hole distance of investigated holes from the target, longest run of adjacent holes, centre crossings, path efficiency, tortuosity. The rules are Gawel et al. 2019, Table 1, and the interface says so beside the answer: a user who cites the paper is citing what the tool actually computed. The interface shows the feature values, the rule that fired, any rule that fired but was outranked, and the runner-up, and it accepts an override with a free-text reason stored as a correction. Dimensionality-reduction views were deliberately excluded: a student defending a classification needs a sentence, not a position in a scatter plot.
Correcting without losing anything (D24–D27). A stacked timeline over one shared time axis —
detection state, confidence, events labelled with hole numbers, corrections, trial markers — above
a frame-accurate scrubber, fully keyboard-navigable. Corrections are point nudges, range tools
("not visible here", "entered the escape box here"), event edits, trial-start adjustment,
strategy overrides and a confirmed non-escape — "the animal never entered the escape box", with a
reason, which is what lets a trial that never escaped read ok instead of being flagged forever —
each stored sparsely and individually revertible. Automatic and corrected
values differ by shape and text as well as colour, so they survive a grayscale printer and a
screen reader. Everything autosaves to IndexedDB on every change, and the session file is the
portable record.
What is hashed, and what is only a cache (D51, D55). The auto layer is keyed by a hash of the
tracking parameters alone, so changing an event threshold does not throw away an hour of tracking,
while a tracking change invalidates everything downstream. The full parameter set is hashed into
every export. The derived layer is a cache: it is recomputed on load and never trusted as
authoritative, because what is not hashed cannot be reproduced and what is recomputed cannot go
stale.
Barnes maze conventions vary between laboratories, so the behavioural definitions cannot simply be looked up — they are lab conventions, not facts. Each one is resolved with an explicit default that is in force, visible in the interface, adjustable, and written into every export as a column, so a later change of mind is a recomputation rather than a reanalysis.
Every one of them was reviewed against Gawel et al. 2019 and Illouz et al. 2020 on 2026-09-07.
Five changed as a result and are recorded as closed decisions D58–D62 in
docs/decisions.md: the search-strategy rules became the paper's, an entry
that runs to the end of the clip stopped needing a minimum duration, gap filling was turned off by
default, path efficiency took Illouz's definition, and every trial row now names its target hole.
The rest were confirmed at their measured defaults, with the reason each closes recorded beside it.
The numbers below are what the tool ships with today.
escaped false and is marked review rather than being given the cutoff as a number — until a
person confirms the animal genuinely never went in, which is the only thing that clears it (D63).
A brief head-in-and-out at the escape hole beside such a confirmation is noted, not treated as a
contradiction (D67). A probe trial has no escape box, so it runs to the cutoff with its escape
measures blank and is never sent to review for not escaping (D68).Five further ambiguities were resolved structurally rather than numerically:
quality.csv; it is never typed in twice. A cohort recorded on two mazes carries two maps.null
until the maze step is finished, the parameter set is null until the first analysis run, and
the derived layer is null until there is something to derive. They are nulls, not placeholders,
because a fabricated value is a lie and autosave has to work from the first file dropped.ffprobe's output.low_confidence with the reason fragmented. Reporting those frames as ambiguous would lose the
events the tool exists to find; merging them silently would hide the uncertainty..slp import onto source: imported — designed for (D8 exists partly to admit it) but
behind the core features in the order set out in D42.reviewer
column; the comparison itself (agreement per event, per trial) is not built, and the field that
sets the reviewer is still to come in the interface.document calls was judged not worth the dependency.The full list, split into defects, deliberately excluded scope, and findings about the sample data,
is in docs/known-limitations.md. The three that matter most:
prototypes/analysis/RESULTS.md, signature checks). Under the recorded sample map, whose target is
hole 7, neither hole is the target: test53's 2.37 s run is long enough to be entry-shaped anyway
and is flagged "physically unlikely — review", while test51's 0.93 s run is not, and stays an
ordinary investigation.
What is still missing is Gawel's actual criterion, the whole body inside the hole; the tracker's
blob has no per-frame mask, so the tool uses the loss of detection as its proxy and says so.derive() from the
track it ships, and a test re-derives the committed document to prove it. The panel says on
screen where the numbers came from. Attach a real file — drop it, or confirm the download in the
example-cohort dialog — and the frames come back.What leaves the user's machine: nothing. BarnesTrack is a static page. Videos are read from disk by the browser and decoded in the tab; tracking, analysis, figures and exports all run locally; the session file and the CSVs are written straight to the downloads folder. There is no server to send anything to, no account, no telemetry, and no third party in the path — there is not even a font or a CDN script to fetch. Loading the page and working through a whole cohort of your own videos issues no request at all beyond the page's own HTML, JavaScript and CSS. Requests exist in exactly two places, and both need a button press:
examples/example-cohort.barnestrack.json.gz (about 500 kB) from
the page's own build — same origin, the same static deployment the page was served from, nothing
leaving it.test50.mp4,
test51.mp4 and test53.mp4, 3.1 MB in total — from the public sample-data repository. These are
the only outbound requests BarnesTrack can make; the dialog names each file and its size before
the first one is sent, its other button loads the same results with no network request at all, and
they exist so the demo has real video to scrub.The other URL-shaped strings in the built bundle are never fetched: XML namespace identifiers — the
SVG namespace passed to createElementNS, and exceljs's OOXML namespaces once the XLSX export is in
the bundle — and the sample-data repository link the provenance line renders as an <a href> for a
reader to follow. That is checkable rather than promised: open the network tab and work through a
whole cohort. This matters because behavioural recordings sit under an animal-use protocol and
plenty of institutional data cannot leave the building; a tool that cannot phone home does not need
to be trusted not to.
Keys and cost: there are none. No API key to obtain, so there is nothing to degrade to when one is missing — the demo path and the full path are the same path. A run costs nothing because the computation happens on hardware the lab already owns, and it does not get more expensive with more videos: sixty videos cost sixty times zero. At scale the only line item is static hosting of a build of a few hundred kilobytes, which is free on every host that would serve it. The design choice that buys this is the same one that answers the data question — no server — and the cost of that choice is paid in browser support (WebCodecs is required) rather than in money.
Once a cohort is exported, the questions that follow are usually comparisons: how did the lesion
group's latencies change across days, which videos need a second look, why do these two
spreadsheets disagree. skills/barnestrack-cohort/ is a Claude skill
that answers them. Add it to Claude Code or claude.ai, point it at an export folder, and ask.
It reads the exported files and nothing else — no server, no connection to the application, no
access to the videos. It works from the three CSVs and the parameter set, and falls back to the
session file in the same folder only when those cannot answer the question. It knows the column
list and the units, reads schema_version before pooling two files, and it is
built around the rules that make a cohort answer honest: it will not fold trials marked review
into a headline number without saying so, it treats a blank cell as "not recorded" rather than
zero, and it refuses to compare two cohorts without first checking that their parameters_hash
values match.
Accessibility is treated as a requirement per feature rather than a pass at the end (D37). What is implemented, and how to check it:
+/−/0 zoom, Alt plus arrows pan, and the scrubber steps a frame at a time. The full key
table is in tests/browser/README.md. To check: unplug the mouse.FigureDescription — the same facts
as text — built to be rendered beside it; the Review step that puts those tables on the page has
not landed, so today they are exercised in the tests and in the development gallery.aria-live="off", so a screen reader is told what happened without a
running commentary.There is no automated accessibility test in the suite. These are checked per feature as it lands,
in the Playwright specs where a real browser is needed, and in the manual pass recorded in
tests/browser/README.md.
Full notices, with licence texts, are in
THIRD_PARTY_NOTICES.md. In short:
From talmolab/vibes (BSD-3-Clause, commit d9410fa),
patterns re-implemented in TypeScript, each in a file whose header names the source tool — run
grep -rn "Adapted from talmolab/vibes" src/ to see every one. Nothing is copied wholesale.
video-player: the sample-table frame-identity model, keyframe-window decoding and avcC
decoder configuration (src/video/mp4-index.ts, sample-reader.ts, frame-source.ts).
Rewritten for mp4box 2.x, and extended with the picture-order-count tie-break (D45) that these
recordings turned out to need.labelroi: the two-layer canvas with its screen-to-video transform and device-pixel-ratio
backing store (src/ui/canvas-view.ts, src/maze/view-transform.ts). Rewritten as pure
functions so the transform can be unit-tested without a DOM.slp-viewer: the worker-side demux/decode core (src/video/track-worker.ts, decoder.ts,
mp4-index.ts). Rewritten around backpressure, synthetic decoder timestamps and luma extraction,
none of which the original needs.event-annotator: the row model and the paint-style editing operations
(src/session/corrections.ts, src/ui/timeline-model.ts, src/ui/timeline.ts,
src/ui/review-step.ts). Rewritten so an edit is an append-only correction record with a source
and a provenance trail rather than a mutation of the row, which is what makes automatic and
corrected values distinguishable in every view and every export.Ideas only, no code: quality tiers from quality-review-tool, and the re-encode guidance from
encoding-helper.
Runtime dependencies: mp4box.js (BSD-3-Clause) for MP4
demuxing, and exceljs (MIT) for the XLSX workbook.
Colour maps: the viridis and cividis anchor points in src/viz/colormap-tables.ts are
sampled from matplotlib's tables (version 3.9.1) and interpolated here. No matplotlib code is
shipped.
MIT — see LICENSE.