thorntonstatistical.com

Five programs · one binary each · nothing to install

TheThorn Tools


An editor, a spreadsheet, a canvas, a shelf of papers, and a workbench for R. Each one is a single file of about eleven megabytes that draws every pixel itself, starts before you have let go of the mouse, and asks the network for nothing — ever.

They share a palette, a preferences folder, a command line, and a conviction that the file on disk should still make sense in ten years without the program that wrote it. Every screenshot below is the real thing, rendered by the binary you can download, set in a handwriting font traced from a page of mine.

I–V

What they hold in common

None of this is configurable, because none of it is a preference. It is what the five programs are.

One file
SDL3, FreeType and HarfBuzz are compiled in. There is no installer, no runtime, no update service and no telemetry. You copy the binary somewhere and run it; you delete it and it is gone.
Drawn
No widget toolkit and no web view. Glyphs are shaped by HarfBuzz, rasterised by FreeType into a single atlas, and every one on screen leaves in one SDL_RenderGeometry call.
Readable on disk
A sheet is tab-separated lines. A map and a shelf are ZIP archives whose first entry is plain text — unzip -p yours.thmap map prints it. Formats you can diff, grep, and put under version control.
The same colon
Every menu item hands the command line the exact string you would have typed, so the menu is a front end to : rather than a second implementation of it. Nothing you can click is unscriptable.
Printing is a file
Ctrl+P writes a PDF — no print dialog, no spooler. The faces are embedded and text goes in as glyph indices, so the page keeps the same letterforms as the screen and stays selectable.
Ink on cream
The screen is ink and gold and oxblood. The paper is not the screen inverted: a document printed in negative wastes a cartridge and reads worse, so the printed page is dark type on warm white.

I

thorn edit

One window, one column of type, one hairline of status. Nothing else.

A text editor for prose. It opens in insert mode and behaves like an ordinary text box until you press Esc, at which point the whole vim vocabulary is there — counts, motions, text objects, .-repeat, visual mode, search. Commands are read as characters rather than key codes, which is why $ and ^ work on a keyboard that is not American.

Undo coalesces a run of typing into one step, so Ctrl+Z walks back by phrases and not by keystrokes. Whatever is on screen is written to a session file every few seconds and restored on next launch, so closing the window without saving does not throw the text away.

A small dragon sits on the page. It breathes, it blinks, its pupils drift toward the text while you type — and it never speaks unbidden. Clicked, it will tell you that your last sentence ran to fifty-one words, or that you have used merely three times in a paragraph. Those observations are computed from your document, locally, and nothing else reads it.

Opens
any UTF-8 text
Prints
PDF, embedded face
Modal
vim-style, optional
Binary
10.5 MB, Windows x64
Licence
MIT
thorn edit showing a page of prose set in handwriting on a near-black ground, with a thin status bar reading: insert, sample.txt, 120 words, 1:1.
IZen mode, ink set to oxblood with Ctrl+K. The status hairline carries the mode, the filename, a word count and the caret position, and nothing else ever appears.

II

thorn sheets

A spreadsheet with a statistician's disposition, and R one keystroke away.

A cell can be an R expression. Not a macro, not an add-in, not a bridge you configure — a formula, sitting in a cell, recalculating when its inputs change, exactly as =SUM() does. =RDF("coef(lm(score ~ hours, df))", A1:B6) fits the model and spills the coefficients into the cells below. Python and Julia get the same treatment. If the expression draws, the picture is the cell.

Nothing is embedded: whichever of R, Python and Julia you already have is started as a child process speaking line-delimited JSON over a pipe, and it stays warm, so two hundred R formulae pay R's startup cost once. If none of the three are installed, everything else still works.

Seventy-five native functions handle the statistics you should not have to leave for, and three of them deliberately disagree with Excel: -2^2 is −4 here as it is in mathematics, SLOPE takes x first, and SKEW reports a population moment. Export to .xlsx translates through the parser rather than by search-and-replace, so the meaning survives even where the text cannot.

Formats
.thsheet · .xlsx · csv
Kernels
R · Python · Julia
Functions
75, native
Headless
--eval · --csv · --check
Binary
10.9 MB, Windows x64

Note. The Julia kernel is written but has never been run — Julia was not installed on the machine this was built on. R and Python are exercised end to end.

thorn sheets with a grid of hours and scores, native statistics in column D, and the same numbers recomputed by R in rows 8 to 11. Small corner marks distinguish the two.
IIThe corner marks are the legend. Gold means thorn sheets worked the number out itself; ember means another language answered. Here the native slope and R's lm() agree to four places, which is the point of the sheet.
An R scatter plot with a fitted regression line, drawn directly into a block of spreadsheet cells in the sheet's own gold and oxblood palette.
IIiiThere is no RPLOT(). A plot is a spill: it takes the cells it needs, refuses to land on anything you typed, reflows when you widen a column, and is drawn in the sheet's palette so it sits on the page instead of glaring off it.

III

thorn mapper

A map of what you know, with the documents inside the file.

Nodes you write in, links between them, and a canvas that never rearranges your thinking between sessions. Position is truth. Ctrl+T tidies the subtree under the selection — but it is a command you invoke, not a constraint the canvas enforces, because auto-layout you cannot refuse turns a map into a diagram of the tool.

Drop a PDF on a node and it goes into the map. A .thmap is a ZIP whose first entry is the line-based, diffable map text and whose remaining entries are your documents, byte for byte. A link can rot; a document cannot. Both exist, deliberately.

Ctrl+P prints two documents into one PDF, and both are wanted: the plate, the canvas as resolution-independent vector art that prints as cleanly at A0 as at A5; and the outline, the same map as indented text with every body in full and an appendix naming each embedded document. A map you can only look at is a poster. A map you can only read is a list.

Format
.thmap (ZIP + text)
Imports
markdown outlines
Exports
PDF · outline · PNG
Embeds
anything, byte for byte
Binary
10.5 MB, Windows x64
A thorn mapper canvas: a root node reading 'Release the thorn tools' with curved links to child nodes for edit, sheets and mapper, each carrying a short body of text, and two further nodes describing the file format.
IIIWhere a link attaches is decided by the gaps between the boxes rather than the distance between their centres — which is why none of these curves leaves an edge and doubles back on itself. Six accent colours, cycled with Ctrl+K; not a colour picker.

IV

thorn cite

A shelf of papers, and the papers are on it.

A reference manager where the PDFs live inside the library file. A .bib beside a folder of papers is two sources of truth that drift — a renamed file, a re-export that drops your notes, a synced folder that half-arrives. A .thshelf is one file you can mail, back up, or open in ten years, with the metadata and the papers together. BibTeX is an import and an export format, not the storage format.

The search box always has the keyboard. Just type, and it filters on every keystroke across author, title, venue, year, key, tags and your notes, because a library is not something you scroll — it is something you narrow. Drop a PDF on the window and it reads the XMP packet, then the Info dictionary, then hunts for a DOI through the inflated content streams, and tells you in the status bar which of the three it believed.

What is stored is not what is shown: the field keeps {\v S}m{\'\i}d so your exported .bib is byte-faithful, while the screen and the printed page say Šmíd — and file it under S, not under M.

Format
.thshelf (ZIP + text)
Reads
BibTeX · PDF metadata
Merges on
DOI, then key
Network
none — see below
Binary
10.6 MB, Windows x64

Gap. No DOI lookup, no Crossref, no arXiv fetch, no sync. Everything is read out of files you already have. This is the biggest thing missing and the next thing worth building.

thorn cite: a sidebar of collections, a list of eight references sorted newest first, and a detail pane showing the selected entry's authors and BibTeX fields.
IVAwkward cases on purpose — curly quotes, an em dash, a Czech surname, an entry with no author, dollar signs inside a title. All of them survive a round trip out to BibTeX and back, which is what the example library in the repository is for.

V

briar

An IDE for R. A page for the code, a console beneath it, and everything else summoned only when called for.

A briar is a thorny wild rose. It is built from the other tools' bones — the same hand-drawn text engine, the same modal editing carried over whole from thorn edit — with a real R prompt underneath: a warm Rscript child, session state persisting across commands, Tab completion through R's own machinery, and ?lm doing what you expect.

Evaluation runs on its own thread, so a ten-minute fit never freezes the page, and Esc is a real interrupt — R's own interrupt condition, caught by the kernel, answered mid-flight, objects intact. Ctrl+Shift+` opens another R session in its own tab; a model fitting in one does not stop the other from answering.

Panels slide in one at a time and leave when you press Esc: environment, plots, help, files, packages — and a copilot that rides the AI subscription you already pay for, by shelling out to claude -p (or codex, or gemini), so no API key lives in any file. And View(df) does not open a grim grey grid: the frame is handed to thorn sheets, which knows what to do with one.

Needs
R on PATH
Terminals
as many as you like
Interrupt
real, mid-flight
Copilot
any stdin/stdout CLI
Binary
11.0 MB, Windows x64
briar showing an R script with line numbers: comments in dim vellum, strings in gold, keywords in ember, numbers in pale gold. The status bar reads insert, sample.R, R --, 144 words.
VSyntax colour in the family palette: comments dim, strings gold, keywords ember, numbers pale gold, calls bone, and the assignment arrows in rose ash. A string that wanders across a line break keeps its colour.

Get them

Downloads

Windows x64, built and verified. Nothing here needs installing: unzip it anywhere and run it.

Program File Size SHA-256 of the binary
thorn edit thorn.exe 10.5 MB ac74e3b6bd0375678c385447e03bee9dc85aa02f791b70bb08d69656d50c4e13
thorn sheets thorn-sheets.exe 10.9 MB 869da680c1c9873e542ec7a7403c415040f095561cd210367c36458ef846447a
thorn mapper thorn-mapper.exe 10.5 MB aac04a246d61f7c84d7963781a4c37a5ec5d802b8699045fa75b29e3ab386457
thorn cite thorn-cite.exe 10.6 MB 94161d4a206a8338a51c4ba9ecfe35b30a07670844446e77b95508f2eff8b369
briar briar.exe 11.0 MB 51b629424e0fb5f828b2c8b31c1bfb859a12fb9af324063f423001f99530241b

Unsigned. These binaries are not code-signed, so Windows SmartScreen will interrupt the first launch — More info, then Run anyway. Check the hash above against certutil -hashfile thorn.exe SHA256 if you would rather be sure than trusting.

Or

Build it yourself

CMake 3.24+, a C++17 compiler, and a few minutes the first time.

# SDL3, FreeType and HarfBuzz are fetched and
# built from source, pinned to known-good tags.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel

./build/bin/thorn            # or build\bin\thorn.exe

The five pin the same dependency versions on purpose, so if you have built one beside the others its clones are reused and the rest configure in seconds. Each stages itself into dist/ — the binary, its assets, and, where there are any, the kernel shims.

Windows is what these are built and verified on: MinGW-w64, GCC 15, UCRT. The release build links the runtime statically and is marked as a GUI subsystem app, so no console window sits behind it.

Linux and macOS builds are written to be portable but have not yet been run. SDL3 wants development headers for your display stack before it will configure; macOS wants Xcode command line tools, and a binary you hand to anyone else wants codesigning.