[thicken, in gold 3D bubble letters, drifting slowly: the logo is a thicken model]
[Latest release]
[npm]
[CI tests]
[In the official MCP Registry]
[On skills.sh]
[On cursor.directory]
[Antigravity plugin]
[On Smithery]
[thicken MCP server, quality and maintenance score on Glama]
[glTF 2.0]
[TypeScript]
[Node 22.18 or later]
[MIT license]
[Buy Me a Coffee]
[Donate to ALS research]
Photos in. Out comes the object: its 3D model and its metadata.
thicken turns photos of a real object into a GLB model at its real size
and its metadata as JSON, read off the object by vision models and looked
up in public catalogs. What it knows about each kind of object lives in a
profile: a few come with thicken, and you can add your own.
[Photos of the front and back of a CGC-graded Giannis Antetokounmpo autograph card on a wooden table, beside the clear 3D slab thicken made from them, turning all the way round]
[Photos of the front and back of a PSA-graded 1976 Topps Julius Erving in a tall holder, beside its 3D model, turning all the way round]
[Photos of the front and back of a reverse-holo Psyduck card, beside its 3D model, the foil shimmering as it turns]
In each, the photos going in and the model coming out, with its metadata, like the one below. The logo above is a thicken model too, made by its lettering profile: thicken lettering thicken.
The other half: its metadata. Part of the first one's, a graded Giannis Antetokounmpo card:
The grader and certificate number come from the label's QR code; the vision
models read the rest off the slab. What each profile records is in
the table of profiles, and every field is described in
USAGE.md.
Contents
Try it
Each run writes <name>.glb and <name>.json beside the first picture, and
with --preview a picture of both sides. Photos straight on and cropped work
as they are; uncropped photos, angled shots and video need a vision roster,
any OpenAI-compatible endpoint (Vision). As a
library: import { thicken } from 'thicken', then thicken(files, options)
gives the GLB's bytes and the sidecar.
From an AI agent
thicken is an MCP server too (thicken mcp), with an Agent Skill that tells
the agent when and how to use it: ask for a model of the book on your desk,
and the agent makes it. In Claude Code:
Or for any MCP client, npx -y thicken mcp on stdio. Codex, Copilot,
Cursor, Antigravity, Gemini CLI and the rest are in USAGE.md.
How it works
[Photos, a video, words or an ISBN go in; thicken matches them to a profile, processes them (the vision models find each face, cut it out and straighten it, and read what's printed), builds the object at its real size, and writes a GLB model and a JSON sidecar.]
What thicken knows about a kind of object lives in its profile: how to
recognize it, its faces, what to read off it, and how to build it at its real
size. A code on the object picks the profile (an ISBN barcode, a grader's QR
code), or the vision models say which it is. thicken is not a photogrammetry
tool: everything it handles is close to a box or a flat outline, so it builds
the geometry from known sizes and maps your photos onto it.
The thicken object
What thicken makes is more than a 3D shape. Each run writes a thicken
object: the model (a GLB at the object's real size, in millimeters) and,
beside it, its metadata (the JSON sidecar), which knows what the object is, in
its profile's own terms. For the profiles that come with thicken, for
example:
- A book: its ISBN, its catalog record (title, authors, publishers,
dates, subjects, Dewey and Library of Congress classes, pages), and how sure
the match is when it was found by title.
- A graded slab: its grader, cert number and verification link, its
holder, and the card and label inside it, each sized and placed. With
vision, the label read: the card's grade (its number and words, "9 MINT",
any qualifier and subgrades), an autograph's own grade when the label
grades one apart from the card ("AUTO 10"), who signed it and when, and
the card as the grader names it.
- A raw card: its size, its thickness in points for a thick card, its
corners and its foil. With vision, what's printed on it: player or
character, team, year, maker, set, number, parallel.
- A box: with vision, its name, brand, edition, rating and year.
- A badge: with vision, what it names, who issued it, its level,
subject, date, any credential number, and every other word on it.
- Every object: what the vision models read off it, and how sure;
which faces are your photos and which are stand-ins,
how each fact was found, what is still worth photographing, and what each
vision model answered.
An app shows the model and reads the sidecar: a library shelf of your books,
a vault of your slabs. The fields are under
What the sidecar records.
What it makes
thicken makes the 3D object; what it knows about kinds of object lives in
profiles, one file each in src/profiles. Anyone can add one
(CONTRIBUTING.md). Every profile there today:
What each one records in its sidecar, beside what every model has (its size and where it came from, its faces and
pictures, its codes, what the vision models read and how sure, what's worth a second look: all in
USAGE.md):
badge: edge, regions, measuredBy
book: isbn, catalog, match, pages, binding, bindingBy, measuredBy, depthEstimates, coverUrl, sticker, pageEdges, finish, finishBy, lookups
card: corners, cornersBy, foil, foilBy, foilLook, foilLookBy, pictureBox, pictureBoxBy, border, borderBy, frame, frameBy, texture, textureBy, pt, format, formatBy, stock, stockBy, finish, finishBy, measuredBy
lettering: text, font, fontLicense, color, measuredBy
slab: grader, cert, verifyUrl, holder, inside, measuredBy
box: barcode, measuredBy, rounding, roundingBy, finish, finishBy
Name the profile, or leave it out and thicken picks one:
- An ISBN barcode on any picture, or
--isbn, means a book.
- A QR code pointing at a grader's site (psacard.com, beckett.com,
cgccards.com, gosgc.com, taggrading.com) means a slab, and says whose.
The sidecar keeps the grader, the cert number from the link, and the link
itself (
verifyUrl).
- So does an older label's Code 128 cert barcode, but only on a photo shaped
like a holder. The grader is then the one the vision models read off the
label; with no vision, a PSA-size holder is assumed, with a warning.
- Artwork with transparency around a shape that isn't just a rectangle means
a badge.
- Otherwise, with vision, the models are shown the
profiles and say which it is, and roughly how big: two agreeing settle it,
as with faces, and
box takes anything box-shaped the others don't cover.
- Anything still unknown is refused with a request to name the profile.
Other barcodes don't count: books carry UPCs, QR codes and library
stickers.
The sidecar's identifiedBy.profile says which way it was chosen: given,
recognized (codes or the artwork), or vision.
Adding a profile is one file in src/profiles, found by itself, nothing
else to change: its name; a few words on what it is and on each of its faces,
which is all the vision models are told; what to read off it (more is better:
the sidecar is for apps to take what they want from); optionally how its codes
or pictures give it away unaided; and how to build it from the photos placed
on its faces. Generic profiles are named for the shape they build (box, and
later perhaps flat or cylinder), not "unknown": a shape thicken can't
build is refused rather than forced into one it can. Contributions welcome:
CONTRIBUTING.md says how.
Pictures are placed on faces by what they show (see
Photos). With no front, Open Library's cover stands in,
marked for review, so one photo of the back is enough to start a book.
What leaves your machine
With THICKEN_VISION_URL set, the photos
themselves (re-encoded, so without their EXIF) go to that host. A LAN server
you control still forwards each photo to the model's provider; a hosted
endpoint sees every photo. With THICKEN_BENCH_URL naming a
Benchwarmers gateway with a vision
collection, the photos go to that gateway, which forwards each to the
providers of every seat it picks. The only other calls out are a book's catalog
lookups (Open Library, the Library of Congress, Google Books), which send its
ISBN, or the title and author the models read off it; never a photo.
Nothing else is sent anywhere, and nothing is fetched at run time but those
lookups: previews draw with model-viewer served from your own disk.
Adding a profile
A profile is one file, found by itself: what the object is, its faces, what
to read off it, and how to build it. Make your own while you use thicken, or
improve one of its own, and offer it back:
The template, templates/profile.ts, works as it is (a
box at the size you give) and says on every field what goes there, the
records it writes included. CONTRIBUTING.md has the rest.
The table above is made from the profiles themselves.
Security
Report a vulnerability privately, as SECURITY.md says. It also
lists what thicken touches: files, the network, and the programs it runs.
Built on
For refractor and holo effects in a viewer, simeydotme/pokemon-cards-css is
a reference only. It is GPL-3.0, so read it and don't copy it.
Development
TypeScript on Node 22.18+, with sharp for images. From a clone it runs
directly (Node strips the types). The npm package is JavaScript built into
dist (npm run build, run by npm pack and npm publish), because Node
won't strip types inside node_modules. Every push installs the packed
package on Windows, macOS and Linux and runs it.
From a clone: npm install, then node src/cli.ts … in place of
thicken …; npm test, npm run check, and npm run readme after
changing a profile.
License
MIT. See LICENSE.
The lettering font, Titan One, is under the SIL Open Font License 1.1
(fonts/OFL.txt).