npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

brain-understanding-visualization

v0.5.0

Published

A reusable React component that renders an MRI/scan-style wireframe brain to visualize concept understanding.

Readme

brain-understanding-visualization

A React component that renders an interactive, MRI/scan-style 3D brain to visualize how well a set of topics is understood. Topics are sectioned into regions on a real (embedded) anatomical brain mesh; learning points glow as colored nodes (red→green by mastery, gray when unassessed); latent-learning freshness renders as hollow rings; hovering shows tooltips and pauses the slow auto-rotation.

Everything is self-contained: the brain mesh ships inside the package — no asset files, no network fetches, no setup beyond installing peers.


Install

npm install brain-understanding-visualization

Peer dependencies (your app provides them — hard requirements):

npm install react@>=19 react-dom@>=19 three@>=0.160 \
  @react-three/fiber@>=9 @react-three/drei@>=10 @react-three/postprocessing@>=3

React 18 is not supported (fiber 9 / postprocessing 3 require React 19).

⚠️ Bundle size — lazy-load this

This package pulls in a WebGL stack (three.js + react-three-fiber + drei + postprocessing). Expect the chunk containing it to add roughly 0.5–1 MB compressed to your bundle (measure with your bundler — the package itself is small; the peers are the weight). Do not import it into your main bundle. The supported pattern:

  1. Isolate the import in its own file — this is what forces the code split; nothing else in your app may import this package:
// components/BrainViz.tsx — the ONLY file that imports the package
import { Brain } from "brain-understanding-visualization";
import type { BrainTopic } from "brain-understanding-visualization";

export default function BrainViz({ topics }: { topics: BrainTopic[] }) {
  return <Brain percentageComplete={0} topics={topics} width={640} height={640} />;
}
  1. Load it dynamically, client-only, with a spinner.

Next.js (including static export):

"use client";
import dynamic from "next/dynamic";

const BrainViz = dynamic(() => import("@/components/BrainViz"), {
  ssr: false, // required: WebGL component, client-only
  loading: () => <Spinner />,
});

Plain React (Vite/CRA/etc.):

import { lazy, Suspense } from "react";

const BrainViz = lazy(() => import("./components/BrainViz"));

<Suspense fallback={<Spinner />}>
  <BrainViz topics={topics} />
</Suspense>;

Because the brain mesh is embedded (decoded synchronously on first render, no .glb/texture fetches), the dynamic-import spinner covers essentially the whole loading story — there is no second async asset phase. The first frame may take a beat for shader compilation; that's it.

Optional extras:

  • Defer until visible: if the brain is below the fold, gate the dynamic component behind an IntersectionObserver so the chunk isn't fetched until the user scrolls near it.
  • Prefetch on intent: call import("./components/BrainViz") in onMouseEnter/onTouchStart of the link leading to the page, so the chunk is warm before navigation.
  • Verify the split worked in your build output: the brain page's first-load JS should jump while other routes stay small.

SSR note: render client-only (ssr: false / mount-gated). On a server (or any WebGL-less environment) the component renders a static placeholder instead of throwing, but mixing that with hydration is not a supported path.


Usage — the Brain component

The simple product-facing interface. This is the recommended entry point.

import { Brain } from "brain-understanding-visualization";

<Brain
  percentageComplete={72}
  topics={[
    {
      id: "synapses", // optional stable id; callbacks fall back to name
      name: "Synapses",
      averageMastery: 70,
      learningPoints: [
        { id: "lp-1", name: "Vesicles", mastery: 85, freshness: "fresh" },
        { id: "lp-2", name: "Receptors", mastery: null, freshness: "stale" },
      ],
    },
    {
      id: "memory",
      name: "Memory",
      averageMastery: 33,
      learningPoints: [
        { id: "lp-3", name: "Hippocampus", mastery: 40, freshness: "needsRefresh" },
      ],
    },
  ]}
  width={640}
  height={640}
  background="#1D3E5F"                       // your page background — rendered flat & exact
  onSelectLearningPoint={(id) => openLesson(id)}
  onSelectTopic={(id) => setActiveTopic(id)} // region clicks (node clicks win)
  selectedTopicId={activeTopic}              // controlled highlight + camera focus
  emptyMessage={null}                        // hide the no-data overlay (ghost brain)
/>;

The background prop renders as a flat, unmodified color across the whole canvas, so the component blends seamlessly into your page. (Tone mapping and vignette — which would shift/darken it — are off by default; both can be re-enabled via config.toneMapping / config.vignette.)

Topic selection & the camera focus animation

  • onSelectTopic fires exactly once when a topic's region is clicked (drags don't fire it; clicks that land on a node fire only onSelectLearningPoint). A pointer cursor shows over regions when the handler is provided.
  • selectedTopicId (controlled) keeps that region highlighted, pauses auto-rotation, and — whenever it changes to a new non-null id — animates the camera around the brain so the region faces the viewer (~2s eased orbit; never through the brain). Clicking a region re-presents it even if it's already selected; to re-trigger from the prop alone, set it to null and back. A user drag cancels the animation instantly, and prefers-reduced-motion snaps without animating.
  • selectedLearningPointId (controlled) renders that node slightly larger at full brightness.
  • Duration is configurable: config={{ focusDurationMs: 800 }} (0 = instant).

Data semantics

| Field | Meaning | |---|---| | mastery: 0–100 | Node color band: 0–25 red, 25–50 orange, 50–75 yellow, 75–100 green. | | mastery: null | Not yet assessed → gray point. | | averageMastery | Authoritative topic score (region tooltip + score-mode colors). It is not recomputed from the points — pass your own weighting. | | freshness | "fresh" solid glowing node · "needsRefresh" bright ring, faded core · "stale" empty outline ring. Also shown as a tooltip line. Derive it in your app from time-since-last-answer. | | id (topic / point) | Optional stable identifier; callbacks and the selected*Id props use it. Without it they fall back to name (names should then be unique). | | emptyMessage | Overlay text for empty/invalid data; null/"" hides the overlay ("ghost brain"). | | percentageComplete | Overall completion 0–100. Accepted, currently not rendered — reserved. |

Interaction out of the box: orbit by dragging, slow auto-rotate (pauses while hovering a node or topic region), hover tooltips (name + score + staleness for nodes; name + topic score for regions), click a node → onSelectLearningPoint fires once with the point's name.

The component is defensive: malformed/empty data renders a placeholder rather than throwing; scores clamp; invalid config fields fall back individually.

Sizing / responsive

  • Explicit width/height (px) renders at exactly that size — recommended; drive it from your own breakpoints. Square reads best on small screens; in wide banners the brain sits centered with margins.
  • With no dimensions it fills the container's width at a 1:1 aspect, capped at 300px — usually you want explicit dimensions instead.
  • Heads-up for scrolling pages: the canvas captures the mouse wheel for zoom.

Customization (optional props)

  • config — partial BrainVisualizationConfig; any field of DEFAULT_VISUAL_CONFIG (exported) can be overridden: background (default #1D3E5F, rendered flat/exact), node size, edge network on/off, region sectioning style + hover opacity, vertex-cloud color mode ("score" colors each region's dot cloud by that topic's average — the default), bloom, optional tone mapping/vignette (off by default), auto-rotation, freshness style ("hollow" | "dim" | "both"), focusDurationMs, and more.
  • palette — partial override of the five colors: red, orange, yellow, green, outOfRange (the gray).
  • surface — your own BrainSurface mesh; omit to use the embedded brain.
  • className — applied to the root container (all internal styling is inline and scoped; your global CSS can't break it, and it can't leak out).

Advanced API

BrainUnderstandingVisualization is the underlying component for hosts that need explicit ids and full control (Concept → Topic[] → LearningPoint[]). Brain is a thin mapping over it — see the package's .d.ts for both prop interfaces.


Developing this package (not needed by consumers)

npm run dev opens a playground: leva controls over every config field, four responsive breakpoint previews, and Export → "copy settings" (copies the live config as JSON). Verification: npm run typecheck + npm run test. See CLAUDE.md for architecture and the embedded-surface regeneration pipeline.

Releasing: publishing to npm is automated via GitHub Actions + npm OIDC Trusted Publishing — never npm publish by hand. Cut a release from main with npm run release:patch|minor|major (bumps, tags vX.Y.Z, pushes; the tag triggers the publish), or use the Release workflow button in the Actions tab. Full process and one-time setup: RELEASING.md.

Changelog

Full history in CHANGELOG.md (shipped with the package). Latest:

0.4.0

  • Distinct selected-region opacity: new config.regionSelectedOpacity (default 0.55) makes a selected topic's region less see-through than a transient hover (config.regionHoverOpacity, 0.35). Additive — no breaks.

0.3.0

Seamless host-page embedding. All changes are additive props/config fields, plus three visual default changes (no API breaks).

  • Flat, exact backgrounds: new background prop on Brain (wins over config.background; default now #1D3E5F). Tone mapping and vignette are now off by default so the background renders exactly as specified with no center-glow/dark-edge gradient — re-enable via the new config.toneMapping and config.vignette.
  • Score-colored regions by default: regionColorMode now defaults to "score", so the region tint — including the hover/selection highlight — matches the topic's average-mastery color (the same encoding as the vertex cloud). The hue-spread identity colors remain via config.regionColorMode: "hue".

0.2.0

Topic selection, camera focus, and host-integration APIs. All additions are optional props — no breaking changes.

  • Camera focus animation ("snap to topic"): when a topic becomes selected — by clicking its region or by the selectedTopicId prop changing to a new non-null value — the camera orbits the brain (eased, along the sphere, never through it) until that region faces the viewer. Default 2000 ms via the new config.focusDurationMs (0 = instant); a user drag cancels it instantly; prefers-reduced-motion always snaps without animating. Auto-rotation is suspended during the flight and stays paused while a controlled selection is active.
  • onSelectTopic (both components): fires exactly once when a topic's region is clicked. Node clicks win (they fire only onSelectLearningPoint), orbit drags don't fire it, and a pointer cursor shows over regions when the handler is provided. Host callback errors are isolated, like onSelectLearningPoint.
  • Controlled selection: selectedTopicId persistently applies the hover-style region highlight (hover wins while active); selectedLearningPointId renders that node slightly larger at full brightness. Unknown/stale ids are silently ignored.
  • Stable ids on the Brain facade: BrainTopic.id and BrainLearningPoint.id (optional). Callbacks and the selected*Id props use the id when supplied and fall back to name otherwise — existing consumers see no change.
  • emptyMessage: override the empty/invalid-data overlay text, or pass null/"" to hide the overlay entirely while the brain mesh still renders (a "ghost brain" placeholder).
  • Flat, exact backgrounds: new background prop on Brain (wins over config.background; default now #1D3E5F). Tone mapping and vignette are now off by default so the background renders exactly as specified with no center-glow/dark-edge gradient — re-enable via the new config.toneMapping and config.vignette.
  • Score-colored regions by default: regionColorMode now defaults to "score", so the region tint — including the hover/selection highlight — matches the topic's average-mastery color (the same encoding as the vertex cloud). The hue-spread identity colors remain via config.regionColorMode: "hue".

0.1.0

Initial release: MRI/scan-style brain with an embedded ~6.8k-vertex anatomical mesh (zero assets/fetches), topic region sectioning with hover highlight and tooltips, mastery-colored learning points (gray when unassessed), per-topic score-colored vertex clouds, latent-learning freshness (hollow-ring/dim styles), the simple Brain facade, full visual configuration via DEFAULT_VISUAL_CONFIG/config, and defensive rendering throughout.

License

MIT. Brain mesh derived from the public-domain MNI152 whole-brain scan.