An independent explainer for soycaporal's ternlight — built to help you actually implement it.

source github.com/soycaporal/ternlight

ternlight
ternlight

Your search box matches letters. Your users type meaning.

A customer types “my card got declined.” Your help article says “payment failed.” Zero results — the two sentences share no words. The usual fix is to rent a hosted AI: an API key, a bill that grows with your success, and your users’ text leaving their machine. ternlight is the other answer: a 4.6 MB file you ship with your page, like an image. It reads a sentence and tells you what it means — in the browser, offline, with no server to call and nothing ever leaving the device.

4.6 MB. In your browser. Offline. No server, no key, no bill.

An independent explainer for Wen Shu Tang (soycaporal)'s ternlight — built to take you from "never seen it" to "ready to implement".

Ecosystems Rust, npmInstall npm install @ternlight/baseStatus v0.1, pre-alpha
ternlight: A single lamp pulses three cold signals across a dark sea, and somewhere on the other shore a sentence arrives whole — 384 dimensions of meaning packed into the blink of a light
01

Your search box can't read

What goes wrong without this?

It matches letters. Your users type meaning.

A customer types "my card got declined." Your help article says "payment failed." Zero results. Not because the answer isn't there — because the two sentences share no words, and every ordinary search — Ctrl+F, a database text query, a keyword index — is really just a very fast letter-matcher. It has no idea those two sentences are the same complaint.

So you reach for the thing that does understand meaning: you send your users' text off to a big hosted AI service. And you inherit the whole bill that comes with it. A network round-trip on every keystroke. A per-call charge that scales with your success. A secret key to store, rotate, and someday leak. A privacy conversation with legal, because your users' words now leave your machine. And a product that simply stops working on a plane.

All of that is the price of one small capability: turning a sentence into something you can compare. The interesting question is whether that capability really has to live in a data center at all.

The problem ternlight: the problem
02

A file you ship, not a service you call

What exactly is it?

ternlight is 4.6 MB. It goes in your page. It never phones home.

ternlight is a single 4.6 MB file (its int4 variant) that a browser downloads like an image. You hand it a sentence. It hands you back 384 numbers.

Those 384 numbers are a location. Picture a map where every sentence ever written has a spot, and sentences that mean similar things sit near each other. "My card got declined" lands in the same neighborhood as "payment failed," even though they share no words at all. In machine learning this list of numbers has a name — an embedding — but all the word means is: a point on a map of meaning. Once two sentences are points, "are these about the same thing?" becomes "how close are these two points?" — which is just arithmetic, and arithmetic is something your laptop already does for free.

The whole engine exposes three things: embed() (sentence in, 384 numbers out), similarity() (two sentences in, one closeness score out), and classify() (a sentence and some labels in, the best-fitting label out). Underneath, it's Rust — a fast, low-level programming language — compiled to WebAssembly, a format browsers can run at close to native speed, with a thin JavaScript wrapper on top.

No server. No key. No network. Works on the plane. The cost of the ten-thousandth user is the same as the first: zero.

The big idea
Package variants — all share the same engine, differ only in how weights are stored
VariantBundle sizeBest for
@ternlight/base (4-bit weights — recommended)~7 MBGeneral use
emb_int8 (8-bit weights)~11 MBHigher fidelity
emb_ternary (packed ternary weights)~5 MBSmallest footprint
emb_fp32 (full-precision weights)~40 MBParity reference, not for production
03

The trick: it never multiplies

What's the clever idea underneath?

Every weight is exactly -1, 0, or +1 — so multiplying becomes subtract, skip, or add.

A neural network is mostly a giant pile of learned numbers, called weights, and running one means multiplying your input against those weights, millions of times over. Multiplication is the expensive operation — it is where nearly all the time, heat, and hardware go. It is the reason "AI" normally means "a data center."

ternlight makes one hard rule: every single weight must be exactly -1, 0, or +1. Nothing in between. And look what that does to the arithmetic — multiplying a number by -1 is just subtract it. By 0 is just skip it. By +1 is just add it. There is no multiplication left to do. The most expensive operation in the entire field has been defined out of existence, and what remains is addition, which is the cheapest thing a computer knows how to do.

Three possible states carry about 1.58 bits of information each — hence the name "1.58-bit" weights, versus the 16 or 32 bits a normal weight eats. That is the whole ballgame. The model that would have been hundreds of megabytes is 4.6 MB. The workload that would have needed a server fits in a browser tab.

This is worth being precise about, because it's the point of the project: the small size and the offline-ness are not optimizations bolted on afterwards. They fall out of the -1/0/+1 constraint. Everything the previous section promised you is a consequence of this one rule.

The aha

Replacing multiply with add/subtract is not an approximation — it is the architecture.

04

What happens to your sentence

How does the machinery work?

Five steps, one straight line, no surprises.

1. Split. Your sentence is chopped into known word-pieces by a tokenizer — a fixed dictionary of fragments the model was trained on, so unfamiliar words get built out of familiar parts rather than failing.

2. Look up. Each piece pulls its starting row of numbers from the weights file.

3. Add, subtract, skip. Those numbers pass through the layers. Each layer is a BitLinear pass — a hardcoded routine that walks the ternary (-1/0/+1) weights and, for each one, either adds the value, subtracts it, or skips it. That's the entire forward computation: no multiplies, no dynamically-built computation graph, one fixed path through the code that you can read top to bottom.

4. Go fast. All the weights sit in one flat, contiguous block of memory — not a tree of objects — and are read in 128-bit SIMD lanes. SIMD means one CPU instruction operating on several numbers at once; a "lane" is one of those parallel slots. Flat memory plus wide lanes is why an add-and-skip engine keeps up.

5. Out come 384 numbers. That's your point on the map. Store it, or compare it against points you already stored — nearest wins.

Architecturally this is deliberately boring, which is the compliment it deserves: a flat weights blob, a hardcoded forward pass, a thin JavaScript wrapper. There is no runtime, no scheduler, no service to page you at 3 a.m.

Architecture
Architecture — modules, components and how they depend on each other.
Data flow
Data flow — how a request moves through the system at runtime.
05

What this unlocks

Where would I actually use this?

ternlight fits anywhere you need meaning-aware comparison and cannot or do not want to call an external API.

In the real world ternlight in use
06

Try it in an afternoon

How do I run it right now?

ternlight is a pure library — there is no CLI to invoke. You need Node.js. You do not need Rust — the WebAssembly binary is pre-built and bundled inside the npm package. The steps below take you from install to a working embed-and-compare program in under five minutes.

npm install @ternlight/base
  1. Install the package Run npm install @ternlight/base in your project directory. npm downloads a self-contained package of about 7 MB. (The 4.6 MB figure is the weights file itself — the engine that reads it, and the vocabulary of word-pieces, make up the rest.) When it finishes you will see a normal npm install summary — no native compilation step, no Rust toolchain required, no extra downloads.
  2. Write a file called search.js Add these lines: ``js const { embed, similarity } = require('@ternlight/base'); async function main() { const vec = await embed('my laptop will not turn on'); console.log('Vector length:', vec.length); // → 384 console.log('First 4 values:', Array.from(vec.slice(0, 4))); const score = await similarity( 'my laptop will not turn on', 'troubleshooting power failures' ); console.log('Similarity score:', score.toFixed(3)); // → a number from −1 to 1 } main(); ` The embed() call returns a Float32Array of exactly 384 numbers. The similarity()` call returns a single number: above 0.8 means the same topic, near 0 means unrelated, negative means opposite.
  3. Run it Execute node search.js. You will see three lines printed to stdout: the vector length (always 384), the first four numbers of the meaning-vector (small decimals summing to a unit-length point in 384-dimensional space), and the similarity score for the two phrases. A score well above 0.5 for those two phrases confirms the engine loaded, tokenized the input, ran the ternary inference pass, and returned a sensible result.
  4. Confirm with the built-in smoke test From the repo root, run node ../eval/benchmarks/smoke.js. A passing run prints timing information (milliseconds per embed call) and similarity scores to stdout. If you see scores and millisecond timings, the engine is working correctly across the full test suite.
  5. What you have A fully local semantic similarity function. No API key, no network call, no Python runtime. The embed() call returns in under 2 ms on modern hardware. The package is entirely self-contained — the vocabulary, weights, and inference engine are all inside the npm package you just installed.
  6. Next step Embed your document corpus once at startup, store the resulting Float32Array values alongside each document, and compare each incoming query vector against them with a dot product (multiply corresponding numbers, sum the results). The document with the highest score is the most semantically relevant result. Because every vector is already L2-normalized (length exactly 1), the dot product equals the cosine similarity — no extra normalization needed.
  7. Run it — and here is exactly what you will see Run node search.js. The whole thing finishes in well under a second, with no network access at all (unplug your wifi and it still works — there is no call to make). You will see: `` Vector length: 384 First 4 values: [ -0.0417, 0.0912, -0.1103, 0.0245 ] Similarity score: 0.614 `` The exact decimals will differ — what matters is the shape: 384 numbers back, and a single similarity score. Read the score like this: above ~0.8 the two sentences are about the same thing; around 0.5 they are loosely related; near 0 or below they are unrelated. There is no universal cutoff — you pick the threshold that suits your data by trying a few pairs.
  8. The five-minute test that actually tells you something Before you build anything, do this: pick two sentences from your own data that mean the same thing in completely different words, plus one that does not belong. Run similarity() on both pairs. If it ranks them the way you would, this fits your problem. That single test tells you more than any benchmark table will. Next step: swap require for an import and the same three functions run unchanged inside a browser — that is the whole point of a 4.6 MB engine with no server behind it.
07

Knowledge pack

Does my AI get it too?

ternlight is indexed as a 384-dimensional meaning-vector knowledge base: 85 passages, 2 components, 83 public symbols. Download the pack to explore the codebase semantically.

# ternlight-knowledge-pack.zip for-ai/ # wire this into your agent ternlight-kb.rvf # 384-dim vector brain (semantic search) ternlight-kb.passages.jsonl # full passage text (search returns TEXT) ternlight-symbols.json # exact public API ternlight-dep-graph.json # what depends on what ternlight-entrypoints.json # build / test / run commands ask-kb.mjs · kb-mcp-server.mjs # CLI + MCP search server for-humans/ # read first ternlight-primer.md # the human orientation
Download the knowledge packRVF vector KB + MCP server — drop it into your own agent.
Give your AI the same understandingternlight-knowledge-pack.zip