What is in the kit
| Part | What it is | Where |
|---|---|---|
| His persona | Who he is, how he talks, and what he never does. The authority on him | persona.md, or as a page |
| His model | Rigged glTF 2.0, in four scenes, with faces and accessories | character/latest/, described by its manifest |
| His pictures | Transparent stills, with no code to run | https://thingy.thingelstad.xyz/character/latest/poses/<name>.webp |
| A frame | An iframe with him in it, with no code to run | https://thingy.thingelstad.xyz/embed/ |
| A component | The <thingy-character> web component, for any framework | https://thingy.thingelstad.xyz/thingy.js |
| His voice | The one spoken voice he has | How he sounds, in the persona |
| What he knows | Jamie's published work, through the Librarian MCP server | Connect your AI on Ask Thingy |
| A model to print | A 180 mm figure, as 3MF and zipped STL | Print Thingy |
| His illustration | The original drawing, in the librarian's jacket. The authority on how he looks. A 220 KB PNG, for og:image and app icons | https://thingy.thingelstad.xyz/thingy-original.png |
To try him without writing anything, use the stage. Choose a scene and turn him, then Bring him along gives you the code, a picture or a link for exactly what you see.
Start with his job
Thingy is the same character everywhere, and each place gives him a job. The job picks the scene and how much of his personality comes through. The jobs he has today are in Where he works:
| Job | Scene | Use scene= |
|---|---|---|
| Librarian | Library | library |
| Builder and record keeper | Workshop | workshop, with accessories for the job |
| Host of Supporting Membership | Concert | concert |
| Narrator of the audio edition | None, he is only heard |
outdoors is a scene with no job yet.
If the project clearly fits one of those, use it, and keep to what the job says: builder and record keeper means the record of what Jamie has built. If you aren't sure it fits, ask Jamie. A new job is Jamie's to give, so ask Jamie what he does there before inventing a role, and add it to the persona first.
Each surface has its own register in the persona, and a new project has none until Jamie gives it one. Until then, borrow the nearest and write down that you did, as docs/THINGY.md does for Things I've Built. Settle whose name a page's entries carry at the same time, because he never writes as Jamie. That file settles it for its site.
Give him words
Whichever model writes as Thingy follows the persona. An agent writing pages or code for a project reads all of persona.md first. It is about 17 KB, and it is meant to be read whole.
- A prompt that runs on every turn can carry less. Ask Thingy's chat keeps a condensed identity for its model. Take it from the persona and say so in the file, so the persona wins when they differ. Never fork him, and never add to him there.
- Your surface adds mechanics, not character. Citations, lengths and formats belong to your project. Who he is does not.
- Facts have owners. He reads a figure from where it lives and never states one from memory. The persona names each owner.
The lines that matter most, all from the persona, which wins if these ever differ:
- He is never Jamie. He never writes as Jamie, and his words never pass as Jamie's.
- He has his own name, byline or box, and his own face, so a reader can always tell it is him.
- He invents nothing: no fact, figure, date, quotation, citation or feeling.
- He is he/him, says "I" when talking with someone, and says plainly that he is an AI agent Jamie built when it matters.
- He answers from Jamie's published work, and declines the rest in character.
To speak, he uses OpenAI tts-1-hd with the voice nova, for his own words only. The persona has the current voice and the hand-offs.
To answer from the archive, connect the Librarian MCP server at https://librarian.thingelstad.com/mcp. Its reference lists every tool. Readers sign in with an active Weekly Thing subscription. An agent can also use Ask Thingy's llms.txt.
Show him
| You want | Use | It costs |
|---|---|---|
| A picture on a page | A still | Under 60 KB, and no JavaScript |
| Him on a page, turnable by whoever visits | The frame | A model of about 2 MB, loaded inside the frame |
| Him inside an app, reacting to it | The component | thingy.js at about 820 KB, and a model of about 2 MB |
| Him in your own 3D scene | The model | A model of about 2 MB, and your own renderer |
Each of these follows the newest Thingy as written here. See Follow him or keep him to hold one still instead.
A still
<img
src="https://thingy.thingelstad.xyz/character/latest/poses/workshop-hello.webp"
alt="Thingy in the Workshop, waving hello"
width="240"
height="240"
/>
The name is <scene>-<pose>.webp, 1000 px square and transparent, with a tool or a face in it where the still has one: workshop-hammer-show.webp, library-rest-delighted.webp.
- Every scene has
present. Outdoors and Concert have only that, named byposterin the manifest, and they show the accessories the scene gives him. Inworkshop-presenthe holds the laptop. - The Library's stills also have faces, and the Workshop's have tools.
stillsfor each scene in the manifest lists them. A still shows theoriginalface unless its name gives another. - For anything with no still, use the frame or the component, which draw any scene, pose, accessory and face. Download picture on the stage also draws one, but that copy is yours to host and no longer follows
latest. - Your policy and your background. A strict content security policy needs the host in
img-src. He is made for a light page: on a very dark one, his legs and shadow sink into it. - If this site is ever down, the page loses the picture, so give it
alttext or an emptyaltthat the page can stand without.
The frame
<iframe
src="https://thingy.thingelstad.xyz/embed/?version=latest&scene=workshop&accessories=hammer&pose=show&face=proud&light=off&motion=off"
title="Thingy in the Workshop"
width="360"
height="400"
loading="lazy"
style="border:0;background:transparent"
></iframe>
It is transparent, with no controls around him, and runs nothing on your page, so a strict content security policy needs only the host in frame-src. It takes version, scene, accessories, pose, face, light, motion and view. Anything version doesn't recognize means the newest model. view=<round>,<up>,<zoom> starts him at an angle, in degrees around him, degrees down from straight above, and zoom, as view=60,80,1.2.
The component
<script type="module" src="https://thingy.thingelstad.xyz/thingy.js"></script>
<thingy-character
version="latest"
scene="library"
pose="hello"
face="idle"
light="off"
motion="off"
style="display:block;width:320px;height:380px"
></thingy-character>
It works in plain HTML or any framework. Change an attribute and he changes.
| Attribute | Values |
|---|---|
version | latest or v0.15, with the v. Anything else is the newest |
scene | library, outdoors, workshop, concert |
pose | See poses. Without one he stands in present, so set it |
face | See faces |
accessories | See accessories. A comma list, or none |
light | on or off. The antenna is lit when he is live, answering now or speaking |
motion | on or off. It obeys reduced motion even when on |
blink | on blinks the faces that can, never under reduced motion |
view | The angle he starts at, written as the frame's view is |
zoom | off leaves the mouse wheel to the page |
poster | An image to show until the model is ready |
view-label | The canvas's accessible name |
asset-base | Another directory to load the models from, when hosting the files yourself |
- Events:
thingy-ready,thingy-change,thingy-viewas he turns,thingy-tapon a press that barely moves, andthingy-fallbackwhen he falls back to a still because 3D could not start. - Methods:
resetView(),hop({ height, direction, turns })to jump and spin, andpicture(size), which resolves to a square transparent PNG of him as he stands now. - Properties:
viewreads where he is now, andmovessays whether the pose has motion to play. - Content security policy: the host needs to be in
script-srcandconnect-src, with'wasm-unsafe-eval'inscript-srcfor the Meshopt decoder, andblob:inconnect-srcandimg-srcfor the textures.
A still that becomes him
For a page that should stay light, show a still first and bring him in when the page is up. Skip the 3D for Save-Data or when WebGL 2 is missing, and the still stays if anything fails. Ask Thingy's front page does this, in thingy-3d.ts.
<div style="position:relative;width:240px;height:240px">
<img
id="thingy-still"
src="https://thingy.thingelstad.xyz/character/latest/poses/library-hello.webp"
alt="Thingy, waving"
width="240"
height="240"
/>
</div>
<script type="module">
const still = document.querySelector("#thingy-still");
const saving = navigator.connection?.saveData === true;
const canDraw = !!document.createElement("canvas").getContext("webgl2");
const later = window.requestIdleCallback ?? setTimeout;
if (!saving && canDraw) {
later(async () => {
await import("https://thingy.thingelstad.xyz/thingy.js");
const thingy = document.createElement("thingy-character");
thingy.setAttribute("version", "latest");
thingy.setAttribute("scene", "library");
thingy.setAttribute("pose", "hello");
thingy.setAttribute("zoom", "off");
thingy.setAttribute("poster", still.src);
thingy.style.cssText = "position:absolute;inset:0;width:100%;height:100%";
thingy.addEventListener("thingy-ready", () => {
still.style.visibility = "hidden";
});
thingy.addEventListener("thingy-fallback", () => {
thingy.remove();
still.style.visibility = "";
});
still.after(thingy);
});
}
</script>
The model
Load https://thingy.thingelstad.xyz/character/latest/<scene>.glb with Three.js GLTFLoader and its Meshopt decoder. Files are served with Access-Control-Allow-Origin: *. They are glTF 2.0 in meters, +Y up and +Z forward, with Meshopt compression and WebP textures, on a shared rig of 40 joints.
- Poses are animation clips named for the pose, at 24 fps. The manifest gives each pose's
stillFrame, the frame to hold for a still pose. - Accessories are skinned nodes named
Thingy_Accessory_<id>, with tool clips named<tool>-<pose>. Show the ones he has and hide the rest. An accessory'sabsentNodein the manifest takes its place when it comes off: without headphones,Thingy_Accessory_earsgives him his ears. - The face is one cell of an atlas on the node
Thingy_Screen: materialThingy_Face, emissive map, 4 columns by 5 rows. The manifest gives each face'scell, and ablinkcell for the faces that can blink.faces.webpis the same atlas, small, for a page offering the faces. - The antenna is the material
Thingy_Beacon.
Poses, faces and accessories
Poses
hello, present, thinking and rest, in every scene. On a page where people can turn him, hello makes the better first pose, since the present hand can read as a rude gesture from the side.
Faces
| Face | What it means |
|---|---|
original | The illustration's wink |
idle | He is ready and listening |
thinking | He is at work |
found-it | He found something real, not merely that an answer finished |
oops | Something went wrong on his side |
delighted | A connection found |
curious | A question he is turning over |
proud | Showing something Jamie built |
eager | Making the case for membership |
sympathetic | A hard subject |
sheepish | He got something wrong and is fixing it |
A face means what it says here, so show found-it only when he found something. There is also off, which is never shown to a reader.
Accessories
What he can have with him depends on the scene. A scene gives him what it gives him unless you say otherwise, so leave accessories off for that. Otherwise name what he has, separated by commas, or say none.
| Scene | Accessories | With none | Unless told otherwise |
|---|---|---|---|
library | None | ||
outdoors | backpack | Off | backpack |
workshop | screwdriver, hammer, drill | Empty | Empty hands |
workshop | laptop | Empty | laptop in present |
concert | headphones | Off | headphones |
Some accessories keep to the poses they were made for:
| In the Workshop | Poses |
|---|---|
screwdriver, hammer, drill | carry, show, work |
laptop | present |
none | hello, thinking, rest |
carry holds the tool at his side, show holds it up, and work raises it to use it. With no accessories, a pose only a tool has brings that tool, so present in the Workshop holds the laptop. Asking for a pose the accessory does not have moves him to its first pose. Tool poses are single frames, so motion has nothing to play. An accessory a scene doesn't have is ignored. To read what he ended up with, use the accessories property or the thingy-change event.
The manifest describes each scene's slots, so a page can offer whatever a new scene brings without changing. A slot holds one accessory at a time.
Follow him or keep him
| You want | Use | Changes |
|---|---|---|
| The newest Thingy, always | version="latest", character/latest/ | When this site does, within five minutes |
| One Thingy, for good | version="v0.15", character/v0.15/ | Never |
| His persona | persona.md | When Jamie approves a change, within a minute |
Use latest unless a page needs one model forever.
Change him here, and this is how it reaches a project:
- On its own: the model, the stills, the component and the frame, for anything following
latest. Nothing to do. - By hand: words a project keeps of its own, such as a condensed prompt or a page in his voice. Each change to the persona opens an issue for Jamie, listing what to reread, drawn from surfaces.json.
Tell Jamie about a new project, so it can join surfaces.json with every still it names. A new model cannot ship without those stills, because this site's own checks fail first.
What this site keeps to:
- Published addresses stay. A numbered version's files are never replaced, and an incompatible change gets a new version. The component draws v0.15 on. The older files, v0.10 and v0.14, stay published for anything loading them itself, with the outfit names they were made with.
- Pose, scene and face names stay.
character/latest/holds what the newest version holds, at the same names.https://thingy.thingelstad.xyz/revision.jsongives the commit and the time of the current build, for a project that wants to notice a change.
Where he works now
Two sites to read as examples:
- Ask Thingy (code) follows
latestwith the component on its front page and a still on every other page. The rules it keeps are in its AGENTS.md. - Things I've Built (code) follows
latestwith stills alone, and keeps its job, scene and register in docs/THINGY.md, a good pattern for a new project to copy.
Terms
Everything here is licensed CC BY-NC 4.0: the persona, this kit, the model, the faces, the stills, the print files and the code. Use him, change him and share him, credit Jamie, and don't use him commercially. The whole licence is in LICENSE.
Credit him like this, linked where you can:
The code that Bring Thingy along gives you already carries that line. For a use the licence doesn't cover, ask Jamie.
Jamie has supported Creative Commons for years. His blog and the Weekly Thing carry its licences too, and the first donation from Weekly Thing Supporting Memberships went to it. In his words: "Copyright law can strangle creativity and culture. Creative Commons is doing good work to enable an alternative path."
A few things here have licences of their own, which NOTICE.md lists: the Inter fonts, Three.js inside the component, and the Creative Commons marks.
For agents
llms.txtindexes this site.- This page is
kit.md, and it holds all of the kit but the persona. Readpersona.mdbefore writing anything in his voice. manifest.jsonis the machine-readable list of scenes, poses, faces, accessories and stills. Trust it over this page if they ever differ.