Sidelong turns chat threads into a sideways deck of topic-colored pages

A new chat interface called Sidelong arranges question-and-answer pairs as pages laid out sideways on the screen. The open page sits in the middle, while older pages collapse into thin vertical strips on the left, forming a row of tabs instead of a long scroll. This layout lets users browse many conversations at once without reading every message.

How the sideways layout works

Each Q&A pair becomes a page. The currently open page occupies the middle of the screen. Pages written earlier shrink into narrow strips on the left side, each retaining a colored mark that identifies its topic. When a stack of twenty pages accumulates, the colored marks let you locate a specific conversation without reading through it. The open page remains full-size; everything else condenses into tabs.

Topic coloring by hue and saturation

Every page gets a color mark. Hue represents the topic, so follow-up questions in the same thread share the same hue. Saturation encodes the classifier's confidence that the new question belongs to the same thread as an existing page. High saturation means the system is confident it's a follow-up; low saturation means the question may be a new topic. This coloring helps users spot thread boundaries at a glance.

No API keys — runs through your Claude subscription

Sidelong does not require separate API keys. Chat runs through the Claude Code CLI you already have logged in. Usage counts against your Claude subscription normal limits, the same as any other claude session. A deck — the collection of pages from one continuous conversation — is one Claude session, resumed turn by turn. There are no extra services or accounts needed beyond what Claude Code already provides.

Two prerequisites to install

Before running Sidelong, you need two pieces of software.

First, Claude Code must be installed and you must run it at least once. Sidelong drives the Claude CLI headless, using the command claude -p. This means the chat session goes through your existing Claude subscription without needing a separate login or configuration.

Second, Ollama must be running for local topic embeddings. The Ollama app is available for macOS and Windows from ollama.com/download. Linux users can install it with curl -fsSL https://ollama.com/install.sh | sh, or by unpacking a release tarball and putting bin/ollama on the PATH. You do not need to pull a model manually; the launcher does that on first run. The embedding model is mxbai-embed-large, about 670 MB.

With Node.js 22+ installed, you can get Sidelong running:

git clone https://github.com/canaanmckenzie/sidelong.git
cd sidelong
npm install
npm start

Running npm start preflights everything: it checks the Claude CLI, starts Ollama if installed but not running, pulls the embedding model on first run, launches the two required servers, and prints the URL sidelong → http://localhost:5173.

Running the pieces by hand

If you prefer, you can start the components individually:

ollama serve          # embeddings → localhost:11434
npm run server        # the thin server → localhost:8787
npm run dev           # the app → localhost:5173

Three processes, any order. You can also pull the embedding model beforehand with ollama pull mxbai-embed-large.

Page marks and navigation

Every page displays one mark: a 12px square on the open page, a 3px spine on collapsed strips. Arrow keys move between pages when the composer isn't focused. Clicking a strip opens it, bringing that page to the center.

The deck persists in your browser's IndexedDB. A reload restores all pages and the Claude session behind them. Follow-ups continue the same conversation the next day. The ⋯ glyph (ellipsis) exports the deck as JSON, imports a saved deck, or starts a new one.

If the server or Ollama is down, questions still get colors from a bag-of-words fallback. The readout says offline, but the chat still functions.

How new questions get their color

When you type a new question, the system places it on the color wheel near the pages it resembles most. Follow-ups inherit their thread's hue. The placement runs three times as more information arrives.

First, on Enter, a bag-of-words cosine similarity places the page instantly. This also works as the offline fallback.

About 30 milliseconds later, the question's embedding is scored against each page's question-and-answer embedding, and the placement is redone. The color cross-fades; the page never snaps to a new position.

When the answer finishes streaming, the page is scored again using both the question and the answer. The verdict is allowed to change if the new information shifts the classification.

The verdict line

Under each question, a line shows the classification result. "follow-up · 90%" means the system is 90% confident this is a follow-up to an existing page. "new topic" carries no percentage, because there is no match to be confident in. If the post-answer re-score changed the verdict, the line keeps the history: for example, "new topic → follow-up · 84%". Hovering the line reveals the raw numbers: nearest page, cosine similarity, and hue values.

Calibration from measurement, not guesswork

The calibration constants came from actual measurement, not arbitrary choices. Running node tools/calibrate.js prints the question-to-page cosine matrix over a fixture deck. The default model is mxbai-embed-large because smaller models tested could not reliably separate "same thread" from "same broad domain". For example, a question about sardines outscored a genuine networking follow-up on the nomic-embed-text model.

Under the hood

Questions go to Anthropic through the claude CLI, exactly as in a normal Claude Code session. No other service is contacted. Embeddings are computed locally by Ollama and stay on your machine. The thin server (server/index.js, about 200 lines) binds to localhost, holds no credentials, and does only two things: spawns claude -p and calls Ollama.

The deck lives in your browser's IndexedDB and in JSON files you export. The source code is plain JavaScript, native ES modules, no framework, no build step, zero runtime dependencies. Development dependencies are exactly typescript and @types/node; .npmrc disables install scripts and pins exact versions. Types are checked by tsc --noEmit (npm run check), and tests run on Node's built-in runner.

The source files each have a clear responsibility:

  • src/jev.js — the classifier and the OKLCH painter; pure functions, oneplace() shared by the word-match and embedding paths
  • src/deck.js — rendering, navigation, the composer, streaming
  • src/md.js — markdown reader (pure parser; the DOM is built via textContent, no injection path)
  • src/hl.js — monochrome syntax highlighting; the design allows no color beyond the topic marks, so code structure comes from weight, slant, and ink level
  • src/chat.js — streaming client for the thin server
  • src/limits.js — the % usage glyph and its readout
  • src/deckmenu.js — the ⋯ glyph: export / import / new deck
  • src/store.js — deck state, persisted to IndexedDB; JSON export/import
  • src/theme.js — the black-on-white and slate-with-cream palettes
  • src/fixtures.js — the five seed pages, also used by the tests
  • src/types.js — the data shapes (JSDoc typedefs)
  • server/index.js — POST /chat spawns claude -p (stream-json in, simplified SSE out), POST /embed calls Ollama, GET /healthz
  • server/sse.js — stream parsing and translation; pure functions
  • tools/start.js, tools/serve.js, tools/calibrate.js — launcher, dev server, calibration

Design constraints and known limitations

One deck at a time. Two open app tabs will race the save — last writer wins. The design requires a Claude subscription; that is the point, not a hidden limitation.

The markdown renderer covers paragraphs, fenced code, headings, lists, blockquotes, and inline code/bold/italic/links. No tables or images.

A hue circle is one dimension, and topics are not one-dimensional. With enough distinct topics, two unrelated threads will eventually sit at nearby angles. This is accepted for v1; the design document HANDOFF.md discusses it.

Milestones 1 through 6 of the plan are done: scaffold, server, chat, classifier, re-color, and persistence. Remaining work includes trackpad paging and strip overflow handling past about 30 pages.

SESSION-NOTES.md tracks the build session to session. The original one-file prototype lives at prototype/sidelong.html.