# Architect Source: https://docs.thrixel.com/architect import { Callout } from 'fumadocs-ui/components/callout'; **When to use Architect:** hard-surface and structured objects - props, furniture, buildings, mechanical parts - where you want an **editable, multi-part** result. The output is a part hierarchy you can later [Edit](/edit) part-by-part or [Auto-fix](/autofix). For organic or photoreal forms (characters, creatures), use [Sculptor](/sculptor) instead. Start a new generation from a text prompt, a reference image, or up to six images. The call returns a `submission_id` you track via [Job Status](/progress). ## Create a model Requires API key. At least one of `task`, `image`, or `images` is required. ### Request Body | Name | Type | Description | | --- | --- | --- | | `task` | string | Natural-language description of the model to create. | | `image` | string | Single reference image — base64 data URL or HTTPS URL. | | `images` | string[] | Up to 6 reference images (base64 or HTTPS URLs). Mutually exclusive with `image`. | | `style_reference_submission_id` | string | UUID of a **completed** submission whose look (palette, materials, finish, level of wear) this one should match. Contributes appearance only - the subject still comes from `task` / `image`. Pass the same id across a set to keep it visually consistent. | | `project_id` | string | Group the submission under a project. | | `model` | string | Architect tier: `architect-plus` (most capable, default) or `architect-balanced`. Omit to use the default. `architect-lite` was retired on 2026-08-04 and now returns `400`. | | `adaptive_thinking` | boolean | Enable extended thinking. Slower but higher quality. Default: `true`. | | `effort` | string | Thinking effort: `high`, `medium`, or `low`. Only applies when `adaptive_thinking` is on. Default: `high`. | `style_reference_submission_id` contributes **appearance only** - materials, palette, finish, how worn the object looks. The subject always comes from your `task` or your `image`; pointing at a wooden boat and asking for a barrel gives you a barrel, finished like that boat. It must be a **completed** submission of yours that still has its artefacts. Otherwise: `404` unknown id, `400` not completed or no artefacts, `403` it belongs to another account. For rules rather than looks - polycount budgets, naming, real-world sizes - attach a [project source](/organize) instead. Text states constraints well, a finished model shows appearance well, and they combine. ### Example Request ### Example Response ```json { "submission_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "queued", "created_at": "2026-04-20T14:32:11Z" } ``` ## Next steps Once the job completes, chain refinement onto its `submission_id`: [Auto-fix](/autofix) to auto-improve it, [Edit](/edit) to change specific parts, or [Detailer & Texture](/detailer) for a high-resolution textured pass. --- # Auto-fix Source: https://docs.thrixel.com/autofix import { Callout } from 'fumadocs-ui/components/callout'; Run an automatic refinement pass on a completed [Architect](/architect) or Auto-fix submission. Thrixel inspects the result and produces an improved version. ## Auto-refine Requires API key. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of a completed Architect or Auto-fix submission. | | `project_id` | string | Inherit or override the parent's project. | | `adaptive_thinking` | boolean | Enable extended thinking for higher quality. Default: `false`. | ### Example Request ### Example Response ```json { "submission_id": "b2c3d4e5-...", "status": "queued", "created_at": "2026-04-20T14:35:22Z" } ``` --- # Build World Source: https://docs.thrixel.com/build-world import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; [Build World](https://github.com/thrixel/build-world) turns one prompt into a playable Unity or Three.js game: your coding agent writes the game, Thrixel generates the 3D assets. It ships as two pieces: - **The skill** - a workflow the agent follows (asset planning, engine setup, iteration loop). Lives in the [`build-world`](https://github.com/thrixel/build-world) repository. - **The MCP server** - [`thrixel-mcp`](/mcp), the tools the agent calls to actually generate assets. On **Claude Code** and **Codex** one plugin installs both. Pick your agent below. Using a different coding agent? The [MCP server](/mcp) works in any MCP-compatible client on its own, and the skill is a folder of markdown that most agents can read from their own skills directory. ## Quick start ### Sign in {/* Windows blocks are fenced as `bash`, not `powershell`: shiki's PowerShell grammar reads the slashes in a URL as division operators and paints them red, which makes every install line look broken. The bash grammar renders these same commands correctly. */} Same for every agent. This opens a page with a code, click Approve: ```bash uvx thrixel-mcp@latest login ``` Needs [uv](https://docs.astral.sh/uv/). Not installed yet? ```bash # macOS, Linux, WSL curl -LsSf https://astral.sh/uv/install.sh | sh ``` ```bash # Windows PowerShell irm https://astral.sh/uv/install.ps1 | iex ``` ### Install The recommended way. One Claude Code plugin carries the skill and the MCP server, machine-wide: ```bash claude plugin marketplace add thrixel/build-world claude plugin install thrixel@thrixel ``` Confirm both pieces landed: ```bash claude plugin list # thrixel@thrixel -> enabled claude mcp list # plugin:thrixel:thrixel -> Connected ``` Then start a session. Run this wherever you keep projects - Claude makes the project folder itself: ```bash claude --permission-mode auto ``` Into Claude Code, not the terminal:
```bash title="In Claude Code" # [!code word:/thrixel\:build-world] /thrixel:build-world build a submarine exploration game in three.js set in a bright, vibrant tropical sea with coral and fish ```
Updates are off by default for third-party plugins. Turn them on once: type `/plugin`, open **Marketplaces** > **thrixel**, choose **Enable auto-update**. If you skip this, the skill notices a newer version and tells you to run `/plugin update thrixel@thrixel`.
Prefer to install the two pieces yourself? Skip the plugin and register each half directly. The start command is the same either way. ```bash # macOS, Linux, WSL # 1. The connector. --scope user covers EVERY project, not just this folder. claude mcp add --scope user thrixel -- uvx thrixel-mcp@latest # 2. The skill. Clone rather than download, so it can update itself later. git clone https://github.com/thrixel/build-world ~/.claude/skills/thrixel ``` ```bash # Windows PowerShell # The path is quoted because ~ is not expanded when PowerShell hands it to git, # and a skill that lands anywhere else is invisible to Claude. claude mcp add --scope user thrixel -- uvx thrixel-mcp@latest git clone https://github.com/thrixel/build-world "$HOME\.claude\skills\thrixel" ``` Confirm: ```bash claude mcp list # thrixel -> Connected ls ~/.claude/skills/ # thrixel listed ``` Update the skill later with: ```bash git -C ~/.claude/skills/thrixel pull ```
Switching from a manual install? Remove the old copies The plugin carries both pieces, so an earlier manual install is now a duplicate and the two collide. Remove it once. ```bash # macOS, Linux, WSL claude mcp remove thrixel rm -rf ~/.claude/skills/build-world ~/.claude/skills/goal-to-game ~/.claude/skills/thrixel ``` ```bash # Windows PowerShell claude mcp remove thrixel Remove-Item -Recurse -Force "$HOME\.claude\skills\build-world","$HOME\.claude\skills\goal-to-game","$HOME\.claude\skills\thrixel" ```
Codex reads Claude Code plugins natively, so the install mirrors Claude Code: ```bash codex plugin marketplace add thrixel/build-world codex plugin add thrixel@thrixel ``` Confirm both pieces landed. `codex plugin list` also prints OpenAI's curated catalog, so read the `Marketplace thrixel` block at the top rather than the long list under it: ```bash codex plugin list # thrixel@thrixel -> installed, enabled codex mcp list # thrixel -> enabled ``` The plugin reports its version as `local`, which is expected: it is versioned by commit rather than by a version number. Start a **new** Codex session - a plugin installed mid-session is not picked up. Run this wherever you keep projects: ```bash codex --sandbox workspace-write --ask-for-approval never ``` That lets Codex work without a prompt on every step while keeping its sandbox on. Plain `codex` prompts per action, which stalls a long build. Then start the line with `$build-world` and describe the game:
```bash title="In Codex" # [!code word:$build-world] $build-world build a submarine exploration game in three.js set in a bright, vibrant tropical sea with coral and fish ```
Insert the skill, do not paste it Type `$build` and press **Enter** to pick `build-world` from the list, then write the description after it. Pasting the whole line at once does not run the skill: Codex only treats it as one when it is inserted from that picker, and the giveaway is the colour - an inserted skill turns blue, pasted text stays plain.
Codex asks before every Thrixel call? Approve the server once Codex gates MCP tools separately from the commands it runs itself, so the launch flags above do not reach them. **Always allow** approves one tool at a time, and Thrixel ships dozens. This covers the whole server in one go: ```bash grep -q 'thrixel@thrixel".mcp_servers' ~/.codex/config.toml || printf '\n[plugins."thrixel@thrixel".mcp_servers.thrixel]\ndefault_tools_approval_mode = "approve"\n' >> ~/.codex/config.toml ``` The `grep` guard matters: appending the same table twice is a TOML duplicate key, and Codex then refuses to start at all rather than ignoring the second copy. Check it landed with `codex mcp get thrixel`, and run it again after any `codex plugin remove` / `add` cycle - reinstalling the plugin rewrites that part of the config and drops the setting. Skip the setting and a headless `codex exec` run does not even prompt: the call is cancelled and the agent carries on without Thrixel.
Prefer to install the two pieces yourself? Also the path to take on a Codex build without plugin support. The command is the same either way: `$build-world`. ```bash # 1. The connector, then two settings on it: a startup timeout that survives a # uvx cold start (Codex allows 10s), and no approval prompt per tool call. codex mcp add thrixel -- uvx thrixel-mcp@latest grep -q startup_timeout_sec ~/.codex/config.toml || printf '\nstartup_timeout_sec = 60\ndefault_tools_approval_mode = "approve"\n' >> ~/.codex/config.toml # 2. The skill, linked where Codex looks for it. git clone https://github.com/thrixel/build-world ~/.thrixel/build-world mkdir -p ~/.agents/skills ln -s ~/.thrixel/build-world/skills/build-world ~/.agents/skills/build-world ``` Confirm: ```bash codex mcp list # thrixel listed ls ~/.agents/skills/ # build-world listed ``` Update the skill later with: ```bash git -C ~/.thrixel/build-world pull ```
Cursor has no CLI for either piece, so both are files you write once. It reads the same `SKILL.md` the agents above use. ```bash # 1. The connector. This writes the file only if you do not have one. If you # already have ~/.cursor/mcp.json, add the "thrixel" entry to your own # "mcpServers" instead - the guard below is silent, not an error. mkdir -p ~/.cursor [ -f ~/.cursor/mcp.json ] || cat > ~/.cursor/mcp.json <<'JSON' { "mcpServers": { "thrixel": { "type": "stdio", "command": "uvx", "args": ["thrixel-mcp@latest"] } } } JSON # 2. The skill. git clone https://github.com/thrixel/build-world ~/.thrixel/build-world mkdir -p ~/.cursor/skills ln -s ~/.thrixel/build-world/skills/build-world ~/.cursor/skills/build-world ``` Restart Cursor. Type `/` in the Agent chat and `build-world` should be in the list; the connector appears under **Settings** > **MCP** as `thrixel`. Then pick `build-world` and describe the game:
```bash title="In Cursor" # [!code word:/build-world] /build-world build a submarine exploration game in three.js set in a bright, vibrant tropical sea with coral and fish ```
On Windows, without WSL Same two steps in PowerShell. Step 2 uses a junction rather than a symlink because symlinks need Developer Mode and junctions do not. ```powershell # 1. The connector. Merge the entry by hand if you already have this file. New-Item -ItemType Directory -Force "$HOME\.cursor" | Out-Null if (-not (Test-Path "$HOME\.cursor\mcp.json")) { '{"mcpServers":{"thrixel":{"type":"stdio","command":"uvx","args":["thrixel-mcp@latest"]}}}' | Set-Content "$HOME\.cursor\mcp.json" } # 2. The skill. git clone https://github.com/thrixel/build-world "$HOME\.thrixel\build-world" New-Item -ItemType Directory -Force "$HOME\.cursor\skills" | Out-Null New-Item -ItemType Junction -Path "$HOME\.cursor\skills\build-world" -Target "$HOME\.thrixel\build-world\skills\build-world" ```
Already set up for Claude Code or Codex? Cursor also reads `~/.claude/skills/` and `~/.agents/skills/`, so a skill you installed for either agent is already visible here. Only step 1 is left to do.
Cloud Agents and remote SSH need a project install Cursor does not carry user-level skills into a Cloud Agent or a remote SSH session, so an install under your home directory is invisible there. Put the skill in the repository instead, and commit it: ```bash mkdir -p .cursor/skills git clone --depth 1 https://github.com/thrixel/build-world /tmp/build-world cp -R /tmp/build-world/skills/build-world .cursor/skills/build-world rm -rf /tmp/build-world ``` The connector goes in `.cursor/mcp.json` in that same repository, with the JSON from step 1.
Updating the skill ```bash git -C ~/.thrixel/build-world pull ```
Gemini CLI reads the same skill format and can install it straight from the repository: ```bash # 1. Register the MCP server for every project. --trust means Gemini runs the # Thrixel tools without asking for confirmation on every call. gemini mcp add -s user --trust thrixel uvx -- thrixel-mcp@latest # 2. Install the skill from the repo. gemini skills install https://github.com/thrixel/build-world --path skills/build-world ``` Start a new session wherever you keep projects: ```bash gemini --approval-mode=yolo ``` Gemini asks whether to trust the folder the first time it opens one. Say yes: in an untrusted folder it quietly downgrades `--approval-mode=yolo` back to prompting on every tool call. That auto-approves tool calls for the whole session. `--approval-mode=auto_edit` is the middle ground: file edits go through, shell commands still ask. Confirm both pieces from inside it: ```text /mcp # thrixel listed /skills list # build-world listed ``` An installed skill registers as its own slash command, so start with `/build-world` and approve it when Gemini asks:
```bash title="In Gemini CLI" # [!code word:/build-world] /build-world build a submarine exploration game in three.js set in a bright, vibrant tropical sea with coral and fish ```
Gemini also activates skills from their description, so if the command does not appear, describe the game and name `build-world` in the sentence instead.
### The command | agent | command | |---|---| | Claude Code | `/thrixel:build-world` | | Codex | `$build-world` | | Cursor | `/build-world` | | Gemini CLI | `/build-world` | ## What happens next Your agent checks your Thrixel account, plans the assets the game needs, and starts building. Keep talking to it in plain English to change things. Give it the most capable model you have access to, with reasoning effort set to high or above. In Claude Code that is `/model` set to Opus 5 or better; every other agent has the same picker in its chat panel. Planning a coherent asset list and wiring a scene together is where a weaker model shows. ## Manage your assets Assets appear in your [Thrixel workspace](https://thrixel.com/create) as they generate, so you can view, edit, and reuse them across projects and engines. ## Usage and credits You can test this workflow using the free Thrixel Cubes included with your Starter account. However, building a full-scale game generally requires a wider variety of assets and rapid iteration that usually exceeds Starter limits. Upgrading to a [Paid Plan](https://thrixel.com/create/#upgrade) unlocks higher parallel job processing capacity and higher generation limits, allowing you to bring your most ambitious ideas to life. You can keep track of your remaining Cubes anytime in [Account Settings](https://thrixel.com/create/#settings/billing). --- # Sign in with Thrixel Source: https://docs.thrixel.com/connect import { Callout } from 'fumadocs-ui/components/callout'; Add a "Sign in with Thrixel" button to your app. Your user approves once in a browser, you get their API key, and you call the API as them. They never see or paste a key. Three requests. No app registration, no client secret, nothing to wait for. ## 1. Ask for a code Call this from your server when the user clicks your button. ```json { "device_code": "kL9x...", "user_code": "WXYZ-4821", "verification_uri_complete": "https://thrixel.com/create/cli-auth?code=WXYZ-4821", "expires_in": 600, "interval": 5 } ``` ## 2. Send the user to approve Open `verification_uri_complete` in a popup or new tab, and show them `user_code` so they can check it matches. They sign in, confirm, approve. If they have no Thrixel account yet, they can sign up on that same page. Keep `device_code` on your server. It is the secret that collects the key. ## 3. Poll for the key Every `interval` seconds, until it returns the key. ```json { "api_key": "sk-thrixel-...", "key_id": "8f3c1a2b-...", "email": "user@example.com" } ``` Store `api_key` against that user. Show them `email` so they know which account they connected. Done. While you wait, poll returns **400** with one of these in `detail.error`: | Error | What to do | | --- | --- | | `authorization_pending` | Not approved yet. Keep polling. | | `slow_down` | You polled too fast. Wait longer, then continue. | | `access_denied` | They declined. Stop. | | `expired_token` | Expired or already collected. Start again at step 1. | ## Complete example ```js // Server side. Returns the user's API key, or throws. async function connectThrixel() { const API = "https://api.thrixel.com/api/v1/cli/device"; const auth = await ( await fetch(`${API}/start`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ client_name: "Your App Name" }), }) ).json(); // Show auth.user_code, and open auth.verification_uri_complete for the user. const deadline = Date.now() + auth.expires_in * 1000; let wait = auth.interval * 1000; while (Date.now() < deadline) { await new Promise((r) => setTimeout(r, wait)); const res = await fetch(`${API}/poll`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ device_code: auth.device_code }), }); if (res.ok) return await res.json(); // { api_key, key_id, email } const { error } = (await res.json()).detail; if (error === "authorization_pending") continue; if (error === "slow_down") { wait += 5000; continue; } throw new Error(error); } throw new Error("expired_token"); } ``` It has full access to that user's account, including their cubes. Never put it in the browser or in a URL. ## Before you ship - **Codes expire in 10 minutes.** If nobody approves in time, start over. - **The key is delivered once.** Store it on the first successful poll. - **Jobs bill to the user**, on their plan and their limits. Not yours. - **On a `401`, run the flow again.** The user can revoke your key from their account, and reconnecting your app replaces the key you were holding. - **Set `client_name` to your app's name.** Your user sees it on the approval screen and in their account afterwards. --- # Detailer & Texture Source: https://docs.thrixel.com/detailer import { Callout } from 'fumadocs-ui/components/callout'; The Detailer takes a completed submission as the source mesh and produces a high-resolution textured model. You can also retexture an existing result without regenerating from scratch. [Remesh](/remesh) the result you already have - it re-decimates the existing mesh and keeps its texture, so it's quicker and cheaper than a second Detailer pass, and you get the same model rather than a new one. This page covers the detail + texture endpoints: - `/api/v1/detailer/submit` — full detail + texture pass. - `/api/v1/texture/*` — texture-only operations on an existing mesh (generate, rebake). --- ## Which one should I use? The Detailer rebuilds the mesh, so it can change the shape you gave it. The texture endpoints never touch geometry. Pick by what you need to keep: | What you need | Use | What you get | |---|---|---| | The geometry exactly as it is, every part and name intact | `POST /api/v1/texture/generate` | **Guaranteed.** Geometry is untouched; only materials are regenerated | | Added geometric detail on a precision or hard-surface model | `POST /api/v1/detailer/submit` with `adherence_level: 12` | **Best effort.** Far more parts survive than at the default, but very small ones can still be lost | | A genuine re-sculpt of an organic or sculptural subject | `POST /api/v1/detailer/submit` (default `adherence_level: 9`) | The silhouette is expected to be reinterpreted | Retexturing costs the same as a detail pass and cannot damage what you have. The Detailer is for when you want the *shape itself* to gain detail. Measured on one 78-part quadcopter blockout with `preserve_parts: true`, same seed and same reference image: at the default `adherence_level: 9`, 28 parts came back and one of the four propellers was missing entirely; at `adherence_level: 12`, 35 parts came back and all four propellers survived at full size. The Detailer is strongest on organic and sculptural subjects (creatures, characters, cloth, plants, rock). It is weaker on precise manufactured geometry: an exactly round cylinder, torus or wheel is re-approximated rather than refined, so it returns measurably less round while carrying many times the triangles. Proportions matter too. A model whose bounding box is far from a cube — a building, a roof, a floor plane, anything long and thin — comes back noticeably worse from both the Detailer and the Sculptor, because the object occupies only a small part of the working volume. For buildings, prefer the texture endpoints. --- ## Detail and texture a model Requires API key. Generate a high-resolution textured mesh from a parent submission. A reference image and prompt guide the texture style; if omitted, they are auto-generated from the parent. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of the submission to detail. | | `image` | string | Reference image (base64 or HTTPS URL). Auto-generated if omitted. | | `mesh` | string | Override source mesh (base64 or URL). Defaults to the parent's GLB. | | `prompt` | string | Text description of the desired appearance. | | `seed` | integer | RNG seed. 0-999,999. **Omit for a random seed each run** (the default). Pin it only to reproduce a previous result: the same seed with the same inputs reproduces it. | | `texture_size` | integer | Output texture resolution. Values: `2048`, `4096`. Default: `2048`. | | `decimation_target` | integer | Target triangle count after decimation. 10,000–250,000. Default: `80000`. To lighten a model you already have, [Remesh](/remesh) is quicker and cheaper. | | `adherence_level` | integer | How closely the output adheres to the source mesh geometry. `0`–`12`. `0` follows the reference image most freely (most creative); `12` returns the source mesh geometry itself, so the pass only adds texture. Default: `9`, which stays close to the mesh while still letting the reference image inform detail. Previously named `coherence_level`, which is still accepted. | | `preserve_parts` | boolean | Experimental. Keep the input mesh's separate parts in the output. When `false`, returns a single merged mesh. **Omit to let the server choose:** on at `adherence_level` `9` or above, off below it, where keeping the parts split through a reshaped silhouette produces artifacts. | | `project_id` | string | Assign to a project. | ### Example Request ### Example Response ```json { "submission_id": "e5f6a7b8-...", "status": "queued", "created_at": "2026-04-20T14:45:30Z", "reference_image_url": "https://api.thrixel.com/api/v1/detailer/reference/e5f6a7b8-..." } ``` --- ## Generate textures for an existing mesh Requires API key. Produce fresh PBR textures from a reference image. Geometry is preserved; only materials change. Works on any completed submission, not just Detailer output. Image priority: user-supplied → cached parent input → auto-generated from the GLB. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of a completed submission to retexture. | | `texture_size` | integer | Output texture resolution. Values: `2048`, `4096`. Default: `2048`. | | `image` | string | Reference image (base64 data URI). Auto-generated from the GLB if omitted. | | `seed` | integer | RNG seed. 0-999,999. **Omit for a random seed each run** (the default). Pin it only to reproduce a previous result: the same seed with the same inputs reproduces it. | | `apply_to_node_names` | string[] | Restrict texturing to specific mesh nodes. Empty = whole mesh. | | `project_id` | string | Assign to a project. | ### Example Request --- ## Rebake textures at a new resolution Requires API key. Re-bake the existing texture at a different resolution. Picks the fastest available method automatically. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of a completed submission. | | `texture_size` | integer | Output texture resolution. Values: `1024`, `2048`, `4096`. Default: `2048`. | | `project_id` | string | Assign to a project. | ### Example Request --- ## Fetch reference image Public. Returns the user-provided or auto-generated reference image used to guide the detailing. PNG. ### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of a Detailer submission. | ### Example Request --- # Download Source: https://docs.thrixel.com/download import { Callout } from 'fumadocs-ui/components/callout'; GLB is produced when a submission completes. Other formats are converted on-demand: either inline via the unified `/download` endpoint (which waits for the result), or asynchronously via the `/convert` job API. --- ## Download a submission asset Public. Unified endpoint for all downloadable artifacts. GLB is always ready; FBX/OBJ/STL/USDZ trigger an on-demand conversion if not yet cached. ### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | ### Query Parameters | Name | Type | Description | | --- | --- | --- | | `format` | string | Asset format. Values: `glb`, `fbx`, `obj`, `stl`, `usdz`, `py`, `thumbnail`, `reference_image`. Default: `glb`. | | `textures` | string | GLB only. `png` (default) or `webp`. | | `draco` | boolean | GLB only. Draco geometry compression. Default: `false`. | | `index` | integer | Index for `reference_image` format (0–5). Default: `0`. | ### Example Request GLB is served straight from storage: Any other format triggers a conversion, so the first request for it may take a few seconds: ## Shipping a GLB to a browser If the model is going into a web page or a browser game, ask for WebP textures: Measured at export on real submissions: | Model | Default (png) | `textures=webp` | `+ draco=true` | | --- | --- | --- | --- | | Geometry-heavy (15% textures) | 7.00 MB | 6.28 MB | 1.18 MB | | Texture-heavy (100% textures) | 18.33 MB | 0.95 MB | 0.91 MB | Textures are where most of the weight is on most models, and WebP is a plain image format the browser decodes natively - nothing extra to install, and a reader that cannot handle it fails visibly. **This is the setting to reach for first.** Thrixel's own web viewer serves exactly this: `textures=webp`, no Draco. ### When to add Draco `draco=true` compresses the geometry rather than the textures, so it only pays off on models that are mostly geometry - the first row above, where it turns 6.28 MB into 1.18 MB. On a texture-heavy model it buys about 4%. `KHR_draco_mesh_compression` is read by three.js (with `DRACOLoader`), Babylon, model-viewer and Blender 4.x, but three.js fetches a ~200KB WASM decoder at runtime and you have to wire it up. A reader **without** a Draco decoder usually does not raise an error - it returns the mesh with every vertex at the origin. Do not hand a Draco file to an unknown tool, and do not feed one back into a pipeline of your own. Both flags are independent, and neither changes the model: same parts, same triangle count, same bounds. --- # Edit Source: https://docs.thrixel.com/edit import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; Apply a natural-language edit to an existing model. Example: "make the lantern glow blue". Part-scoped edits work best on [Architect](/architect) results, which carry an editable part hierarchy. ## Modify a model Requires API key. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of the submission to modify. | | `modification_request` | string (required) | Description of the change to apply. | | `project_id` | string | Inherit or override the parent's project. | | `adaptive_thinking` | boolean | Enable extended thinking for higher quality. Default: `false`. | | `focus_on_node_names` | string[] | Part names from the parent mesh to scope the edit to. Every part outside the list is held bit-identical. Empty/omitted = whole-mesh edit. Get part names from `/api/v1/{submission_id}/hierarchy`. | ### Example Request Every part not named in `focus_on_node_names` is left bit-identical to the input. ### Example Response ```json { "submission_id": "c3d4e5f6-...", "status": "queued", "created_at": "2026-04-20T14:38:01Z" } ``` --- # Getting Started Source: https://docs.thrixel.com/getting-started import { Callout } from 'fumadocs-ui/components/callout'; If Claude Code, Codex, or Gemini is the one making the calls, install the [MCP server](/mcp) instead of writing a poll loop by hand. It wraps these same endpoints and handles the waiting. ## What is Thrixel? Thrixel turns text prompts and reference images into production-ready 3D models through a simple REST API. You submit a request, poll (or stream) until it's ready, then download the result in GLB, FBX, OBJ, STL, or USDZ. ## Base URL ```bash https://api.thrixel.com/api/v1 ``` ## Authentication Authenticated endpoints require an API key passed as a Bearer token, in the format `sk-thrixel-...`. To create one, sign in at [thrixel.com/create](https://thrixel.com/create), then open **Profile > API Keys > Create new Key**. Copy the key right away, it is only shown once. ```bash Authorization: Bearer sk-thrixel- ``` Public endpoints (info, stream, downloads, convert, gallery) do not require authentication. Do not ask your users to paste a key. [Sign in with Thrixel](/connect) lets them approve your app in a browser and hands you their key directly. ## Rate Limits Each API key is subject to three limits (defaults shown — your plan may differ): - **Concurrent jobs** — 3 active (queued or processing) submissions at a time - **Hourly** — 10 requests per rolling hour - **Daily** — 50 requests per rolling 24 hours ## Quickstart Submit a text prompt, wait for the job to complete, and download the GLB: **1. Submit a prompt.** The response carries a `submission_id`. **2. Poll until it is done.** Repeat every few seconds until `status` is `completed` or `failed`. No key needed. For a push feed instead of a loop, see [streaming progress](/progress). **3. Download the GLB.** --- # Image Hub Source: https://docs.thrixel.com/imagehub import { Callout } from 'fumadocs-ui/components/callout'; Generate images from a text prompt and reuse them as the reference for a 3D model. Generate once, then feed the same image into as many [Sculptor](/sculptor) or [Detailer](/detailer) jobs as you like. --- ## Generate images Requires API key. Queue one or more text-to-image generations. Each returns an `id` you can poll for completion, then download or reuse. ### Request Body | Name | Type | Description | | --- | --- | --- | | `prompt` | string | Natural-language description of the image. Required. | | `n` | integer | Number of images to generate, 1–4. Default: `1`. | | `size` | string | `1024x1024` (default), `1536x1024`, or `1024x1536`. | | `reference_image` | string | Optional image to guide generation — base64 data URL or HTTPS URL. | ### Example Request ### Example Response ```json [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "queued", "size": "1024x1024", "url": null } ] ``` --- ## Retrieve your images Requires API key. Lists your images, newest first. Poll until `status` is `completed`; the `url` is then the image file (also served directly at `GET /api/v1/imagehub/{image_id}/file`). ```json [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "completed", "size": "1024x1024", "url": "/api/v1/imagehub/a1b2c3d4-e5f6-7890-abcd-ef1234567890/file" } ] ``` --- ## Use an image to create 3D Pass an image's `id` as `reference_image_id` to [`/sculptor/submit`](/sculptor) or [`/detailer/submit`](/detailer) to turn it into a 3D model. The stored image is reused as the reference — no new image is generated. --- # Thrixel API Source: https://docs.thrixel.com/ The Thrixel API turns text prompts and reference images into production-ready 3D models through a simple REST API. You submit a request, poll (or stream) until it's ready, then download the result in GLB, FBX, OBJ, STL, or USDZ. ## Where to start - [Getting Started](/getting-started) — base URL, authentication, rate limits, and a quickstart. - [MCP Server](/mcp) — drive all of this from Claude Code, Codex, or Gemini. **Generate 3D** — pick the engine that fits your object: - [Architect](/architect) — editable, multi-part models (hard-surface, props, structured objects). - [Sculptor](/sculptor) — dense, organic meshes (characters, creatures, photoreal forms). - [Upload a mesh](/upload) — bring your own GLB into the pipeline. - [Image Hub](/imagehub) — generate 2D reference images, then feed them to either engine. **Refine** — improve any result: - [Auto-fix](/autofix) — automatic refinement pass. - [Edit](/edit) — natural-language edits, optionally scoped to specific parts. - [Detailer & Texture](/detailer) — high-resolution detailing and texture generate/rebake. - [Remesh](/remesh) — re-decimate to a new triangle count. **Operate & guides:** - [Writing Prompts](/prompts) — what works and what doesn't, for users with no 3D background. - [Job Status](/progress) — polling, streaming (SSE), and submission listing. - [Download](/download) — get the model out in GLB, FBX, OBJ, STL, or USDZ. - [Projects & Sources](/organize) — group submissions, attach style references, rename/archive/pin. ## Base URL ```bash https://api.thrixel.com/api/v1 ``` --- # MCP Server Source: https://docs.thrixel.com/mcp import { Callout } from 'fumadocs-ui/components/callout'; Thrixel is built agent-first. `thrixel-mcp` gives coding agents a native way to create, edit, and work with 3D assets, in Claude Code, Codex, Gemini, and any other [MCP](https://modelcontextprotocol.io)-compatible client. It exposes Thrixel as tools an agent can discover and use as part of a larger build, and it runs on the same REST API documented here. What the server adds is the lifecycle. It holds the operation open while the work runs, turns HTTP errors into a next action, writes the GLB to disk, and hands back a thumbnail. An agent can generate an asset, see what it made, modify it, bring it into a scene, and keep building, all in one pass. - [**PyPI**](https://pypi.org/project/thrixel-mcp/) - `thrixel-mcp` - [**Example**](https://github.com/thrixel/build-world) - Build World, one prompt to a playable Unity or Three.js game ## Install Nothing to clone or build. [`uvx`](https://docs.astral.sh/uv/) fetches the published package on each launch, so `@latest` keeps itself current. ```bash uvx thrixel-mcp@latest login ``` That prints a link and a short code. Open it, confirm the code matches, approve. The key lands in `~/.thrixel/credentials.json`. Sign-up happens on that page too if you do not have an account yet, so this works from a cold start. Then register the server with your agent: ```bash claude mcp add --scope user thrixel -- uvx thrixel-mcp@latest ``` `claude mcp add` defaults to `--scope local`, which registers the server for the current directory only. Every new project then starts with no Thrixel tools. User scope registers it once for the whole machine. An MCP client loads its servers once, at session start. Register before you open the agent, or restart the session afterwards. ## What it exposes 21 tools, all backed by endpoints on this site. They are intent-level rather than endpoint-level, which is the one place the shapes differ: an agent asking to cut a triangle count does not know that a detailed mesh has to go through remesh while everything else goes through decimate, so `thrixel_reduce_triangles` reads the parent's phase and routes for it. | Doing | Tools | |---|---| | Generate | `thrixel_create_model`, `thrixel_sculpt_model` | | Refine | `thrixel_autofix_model`, `thrixel_edit_model`, `thrixel_detail_model`, `thrixel_retexture_model`, `thrixel_reduce_triangles`, `thrixel_group_parts` | | Inspect | `thrixel_inspect_model`, `thrixel_job_status`, `thrixel_list_assets` | | Organize | `thrixel_start_project`, `thrixel_list_projects`, `thrixel_add_project_source`, `thrixel_list_project_sources` | | Export | `thrixel_download` | | Account | `thrixel_account_status`, `thrixel_pricing`, `thrixel_buy_cubes`, `thrixel_upgrade_plan`, `thrixel_billing_portal` | Waiting uses [SSE](/progress) with polling as a fallback, so a long detail pass reports progress instead of going silent. ## Using the API directly The MCP server is the agent-native path, not a gate. Every capability is a plain HTTP call: see [Getting Started](/getting-started) for authentication and a quickstart, and [Job Status](/progress) for the polling and streaming contracts the server implements internally. --- # Projects & Sources Source: https://docs.thrixel.com/organize import { Callout } from 'fumadocs-ui/components/callout'; Group related submissions into projects, give them nicknames, pin favorites, and archive older work. All endpoints below require authentication and act on resources owned by your API key. ## Projects --- ### List projects Requires API key. Returns all projects owned by the authenticated API key. #### Query Parameters | Name | Type | Description | | --- | --- | --- | | `include_archived` | boolean | Include archived projects. Default: `false`. | #### Example Request #### Example Response ```json { "projects": [ { "id": "p1q2r3s4-...", "name": "Gargoyle variations", "description": "Stylistic experiments", "is_archived": false, "created_at": "2026-04-18T09:00:00Z", "updated_at": "2026-04-20T14:00:00Z", "submission_count": 7 } ], "count": 1 } ``` --- ### Create a project Requires API key. #### Request Body | Name | Type | Description | | --- | --- | --- | | `name` | string (required) | Project name. 1–255 characters. | | `description` | string | Optional description. Max 2000 characters. | #### Example Request --- ### Get a project Requires API key. Returns project metadata plus an embedded list of its submissions (soft-deleted excluded). #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | --- ### Update a project Requires API key. Rename or re-describe. At least one field required. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `name` | string | New name. 1–255 characters. | | `description` | string | New description. Max 2000 characters. | --- ### Delete a project Requires API key. Permanently deletes the project. Submissions in the project are unlinked (`project_id` set to `null`) — they are not deleted. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | ## Project Sources --- Each project can carry up to **256 KB of plain-text reference material** split across files (max **64 KB per file**). Allowed extensions: `.md`, `.markdown`, `.txt`, `.text`. When you submit a new generation inside the project, the parsed text of every source file is used as shared context for every generation in the project — so this is where you put your style guide, glossary of in-world names, dimensions you want respected ("chairs are always 45 cm tall"), or any other context the generation should treat as constant. Drop a `style.md` in a "Gargoyle variations" project that describes your preferred polycount, the materials you want, and the silhouette rules. Every generation prompt in that project will be evaluated against those constants without you re-typing them. ### List source files Requires API key. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | #### Example Response ```json { "project_id": "p1q2r3s4-...", "sources": [ { "id": "src-9a8b...", "project_id": "p1q2r3s4-...", "filename": "style.md", "mime_type": "text/markdown", "size_bytes": 1842, "created_at": "2026-04-20T14:00:00Z", "guard_verdict": "ok", "is_applied": true } ], "count": 1, "total_bytes": 1842 } ``` `total_bytes` is the sum across the whole project — useful for checking how much of the 256 KB budget is left before uploading more. --- ### Get a single source Requires API key. Returns the metadata plus the **parsed text** of the file (what actually gets used as context — line endings normalized, content decoded UTF-8). #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | | `source_id` | string (required) | UUID of the source file. | --- ### Upload a source file Requires API key. `multipart/form-data`. The file is parsed at upload time and stored securely. The parsed content is provided as shared context for every new submission created in the project. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `project_id` | string (required) | UUID of the project. | #### Form Fields | Name | Type | Description | | --- | --- | --- | | `file` | file (required) | `.md`, `.markdown`, `.txt`, or `.text`. Max 64 KB per file. | #### Example Request #### Errors | Code | Meaning | | --- | --- | | `400` | Empty file, or invalid UTF-8 content. | | `413` | File exceeds 64 KB, or the project would exceed 256 KB total. | | `415` | Unsupported file extension. | | `422` | Rejected by the content screen - see below. | A source file's text is placed in the system prompt of **every** generation in the project, so each upload (and each in-place edit) is screened once for attempts to manipulate the model rather than describe your project. A rejection is a `422` whose `detail.message` says what to remove; nothing is stored. Forceful project rules are fine and are the point of the feature - "ALWAYS use flat colours", "NEVER add a ground plane", "every asset must stay under 5000 triangles" all pass. What does not pass is text aimed at the system rather than the model being built: overriding its instructions, asking it to reveal them, or asking it to read or emit anything outside the 3D asset. Every source carries the result: | Field | Meaning | | --- | --- | | `guard_verdict` | `ok` once screened. Anything else means it has not been cleared. | | `is_applied` | Whether this file's text actually reaches your generations. | **Check `is_applied`.** A file can be stored and still be inert - screening has not finished, or it was flagged. Its text is not in your prompts until this is `true`. Screening applies to what you send us. Do not put credentials or private notes in a source file: its contents are given to the model on every run. --- ### Update a source file Requires API key. Rename or rewrite the file's content. At least one of `filename` / `content` must be present. Per-file and per-project caps still apply. #### Request Body | Name | Type | Description | | --- | --- | --- | | `filename` | string | New filename including an allowed extension. | | `content` | string | Replacement UTF-8 text. | --- ### Delete a source file Requires API key. Removes the source file from the project. Submissions already created in the project are unaffected; only **new** submissions after this point will see the reduced source set as context. ## Submission Management --- ### Set or clear the nickname Requires API key. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `nickname` | string \| null | Display label. Pass `null` to clear. Max 255 characters. | --- ### Archive or unarchive Requires API key. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `is_archived` | boolean (required) | `true` to archive, `false` to restore. | --- ### Toggle visibility Requires API key. Hides or shows the submission in list endpoints without deleting it. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `is_visible` | boolean (required) | `true` to show, `false` to hide. | --- ### Pin or unpin Requires API key. Pinned submissions sort to the top of lists. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `is_pinned` | boolean (required) | `true` to pin, `false` to unpin. | --- ### Assign to a project Requires API key. Move the submission into a project, or pass `null` to remove. Both the submission and the target project must belong to your API key. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | #### Request Body | Name | Type | Description | | --- | --- | --- | | `project_id` | string \| null | UUID of the target project, or `null` to unassign. | --- ### Soft-delete a submission Requires API key. Hides the submission from listings but preserves its data. Include soft-deleted items in `/submissions` via `include_deleted=true`. #### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID of the submission. | --- # Job Status Source: https://docs.thrixel.com/progress import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; Once a submission is created, monitor its lifecycle by polling `/info`, streaming `/stream` (Server-Sent Events), or listing all your submissions. --- ## Get submission metadata Public. Returns current status, input, and parent chain. Use this for simple polling. ### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID returned by any submit endpoint. | ### Example Request ### Example Response ```json { "submission_id": "a1b2c3d4-...", "status": "completed", "model": "architect", "input": { "type": "text", "task": "a small stone gargoyle holding a lantern", "image": null, "images": null, "modification_request": null }, "parent_submission_id": null, "nickname": null, "is_archived": false, "is_visible": true, "is_deleted": false, "is_pinned": false, "adaptive_thinking": false, "created_at": "2026-04-20T14:32:11Z", "completed_at": "2026-04-20T14:34:02Z" } ``` --- ## Stream progress (SSE) Public. Server-Sent Events stream that emits progress updates, log messages, and status transitions in real time. Closes automatically when the submission reaches a terminal state. ### Path Parameters | Name | Type | Description | | --- | --- | --- | | `submission_id` | string (required) | UUID returned by any submit endpoint. | ### Example Request ```bash curl -N https://api.thrixel.com/api/v1/a1b2c3d4-.../stream ``` ```javascript const es = new EventSource( "https://api.thrixel.com/api/v1/a1b2c3d4-.../stream" ); es.onmessage = (e) => { const event = JSON.parse(e.data); console.log(event.type, event); }; ``` Event frames arrive as `data:` lines containing JSON. Common shapes: ```json // Progress update {"type": "progress", "percent": 0.42, "message": "Generating geometry..."} // Status change {"type": "status", "status": "processing"} // Terminal event — stream closes after this {"type": "status", "status": "completed"} ``` --- ## List your submissions Requires API key. Returns all submissions owned by the authenticated API key. Filter by status or project. ### Query Parameters | Name | Type | Description | | --- | --- | --- | | `status` | string | Filter by status. Values: `queued`, `processing`, `completed`, `failed`. | | `project_id` | string | Filter by project. | | `include_deleted` | boolean | Include soft-deleted submissions. Default: `false`. | | `limit` | integer | Max results. Default: `50`. | ### Example Request ### Example Response ```json { "submissions": [ { "submission_id": "a1b2c3d4-...", "status": "completed", "nickname": null, "is_pinned": false, "project_id": null, "created_at": "2026-04-20T14:32:11Z" } ], "count": 1 } ``` --- # Writing Prompts Source: https://docs.thrixel.com/prompts import { Callout } from 'fumadocs-ui/components/callout'; import PromptRefiner from '@/components/PromptRefiner'; You don't need any 3D background to write good prompts, but a few wording patterns swing the result dramatically. This page is a cookbook of recipes that work — same rules whether you're describing something new or editing an existing model. Type a rough draft, optionally drop a screenshot of your current model (and a reference image of what you want), and a refined version comes back that you can copy straight into Thrixel. The widget applies the same rules described below. --- ## Five rules that apply everywhere **1. Numbers beat adjectives.** "Decimate to 50,000 triangles" works; "make it simpler" gambles. "Scale to 2 meters tall" works; "make it bigger" doesn't say how. If you can put a number on it, do. **2. Name the part, don't describe it.** Once a model is generated, open the **Scene Hierarchy** panel — every named group (Body, Wheels, Chimney) is a handle you can refer to. Prompts that reference part names ("scale up the Wheels group by 1.5x") behave dramatically better than spatial guesses ("make the round things at the bottom bigger"). In the web app, you can also **click a part in the 3D viewer** to scope an edit to that part. A chip appears above the Edit prompt ("Apply to: Wheels"), and when you Apply Edit, every other part is left bit-identical. Same applies to the Refine button — it'll rewrite your draft to stay within that scope rather than expanding to the whole model. Via the API, pass `focus_on_node_names: ["Wheels"]` on `POST /api/v1/edit/submit` or `POST /api/v1/prompts/refine` to get the same behaviour. **3. Anchor scale and direction to known things, not axes.** "The size of a coffee mug" is unambiguous. "Facing the camera" beats "rotated 90° on Y" — *forward / left / front / behind* gets re-projected correctly more reliably than raw axes. **4. One change per edit.** The single biggest mistake. If you say "make the wheels red AND raise the body AND add a roof", at least one of those three will silently revert or interact badly. Split into three sequential edits; editing is iterative by design. **5. "Keep X unchanged" is a real instruction.** Without it, a small change can pull other parts along with it. Adding "keep the body, smokestack, and tender exactly as they are" ties everything else down. --- ## Creating a model A new-from-scratch prompt has to invent everything — geometry, proportions, materials, parts — so specifics buy you a lot. A useful prompt usually has three components: - **Subject** — what the object actually is. - **Style** — visual aesthetic (low-poly, stylized, realistic, voxel, cartoon). - **Scale anchor** — a reference for size, if it matters. "A wooden barrel with iron rings, low-poly stylized, the size of a person." "A cool barrel." The bad version leaves style, materials, scale, and detail level entirely unconstrained. Each of those guesses will surprise you. ### When you have a reference image If you upload an image, the prompt's job changes — it describes *intent*, not appearance. Use the prompt to call out: - The **focus** in the image (e.g. "model the dragon, ignore the background rocks"). - Anything the image doesn't show (e.g. "the back side has a tail similar to the front legs"). - Style overrides ("the image shows a real dog, but model it low-poly"). "Model the central character only. Stylized hand-painted look. The arms not visible in the image should match the legs." "Make this." *(prompt is empty, image attached)* The empty-prompt case actually works *okay* most of the time, but framing is guessed and the entire scene sometimes gets modelled rather than the subject you cared about. ### Style vocabulary | What you want | What works in the prompt | | --- | --- | | Cartoon look | "stylized", "exaggerated proportions", "no surface detail", "flat colors" | | Realistic look | "PBR materials", "photoreal", "fine surface detail" | | Game-asset look | "low-poly", "stylized PBR", "clean topology" | | Toy / blocky look | "voxel-style", "blocky geometry" | | Specific colors | Hex codes (`#a83232`) or named colors ("dark forest green") | --- ## Editing a model Edit prompts are imperative — they describe *what to change* on top of the existing model. The structure is already known, so you can refer to specific named parts and they'll be matched. ### Geometry edits | Goal | Recipe | | --- | --- | | Reduce triangle count | `Decimate the mesh so that there are 50000 triangles.` | | Uniform resize | `Scale the entire model up by 1.5x.` | | Anisotropic resize | `Scale only the height by 2x. Keep width and depth the same.` | | Move a part | `Move the Chimney 0.3 units forward. Keep everything else unchanged.` | | Rotate a part | `Rotate the Wheels group by 30 degrees.` | | Add a part | `Add a small handle on the right side of the Body, near the top.` | | Remove a part | `Remove the Antenna group entirely.` | | Smooth surfaces | `Apply smooth shading to the Body and Roof groups.` | ### Color and material edits | Goal | Recipe | | --- | --- | | Change one part's color | `Make the Wheels red (#cc2222). Keep the Body color unchanged.` | | Change material type | `Change the Body material from matte plastic to polished chrome.` | | Add an accent color | `Add a thin gold trim around the rim of the Hat.` | "Reduce the polygon count of the Body group to 8000 triangles. Keep the Wheels and Chimney unchanged." "Make it less complex. Also bigger. And the wheels should be black." The bad version mashes three unrelated edits into one prompt. Run them as three separate edits and you'll keep all three. ### Common phrases that misfire - **"Make it look better"** — there's no signal for what's wrong. Tell it. - **"Add more detail"** — *where*? On which part? At what scale? - **"More realistic"** — name the material instead ("rough cast iron", "polished chrome", "weathered painted wood"). - **"A bit bigger"** — give a number or a multiplier. - **"Like a [thing] but cooler"** — describe the cool part directly. --- ## Texturing — material vocabulary When you're dialing in surface appearance (the Texture / Detailer steps), prompts are usually shorter — 2 to 6 words — and a reference image does most of the work. For texture work, **paste or drop a screenshot of what you want before typing a prompt**. The reference image dominates appearance; the prompt is a tiebreaker on material vocabulary. Long prompts without a reference image rarely beat short prompts with one. These short phrases work better than full sentences: | Surface | Phrase that works | | --- | --- | | Old / aged | `weathered`, `oxidized`, `peeling paint`, `green patina` | | Smooth metal | `polished chrome`, `brushed steel`, `gold leaf` | | Rough metal | `cast iron`, `rusted steel`, `corroded bronze` | | Wood | `rough wood grain`, `dark walnut`, `whitewashed pine` | | Stone | `polished marble`, `weathered sandstone`, `mossy granite` | | Plastic / paint | `matte plastic`, `glossy enamel paint`, `flat acrylic` | | Cloth / leather | `worn leather`, `faded denim`, `quilted cotton` | | Glass / glow | `frosted glass`, `tinted glass`, `glowing emissive panel` | "Weathered bronze with green patina, especially in the recessed details." "Make it look real and old." --- ## Putting it together A typical newbie-friendly flow: 1. **Create** with a subject + style + scale anchor. Don't sweat materials yet. 2. **Auto-refine** (no prompt) to clean up obvious geometry issues. 3. **Edit** in one or more passes — one focused change per prompt, referencing part names from the Scene Hierarchy panel, with explicit "keep X unchanged" for everything you're not touching. 4. **Texture** with a short material descriptor and (ideally) a reference image. If something misfires, the fastest fix is almost always to **shrink the prompt**, not lengthen it. --- # Remesh Source: https://docs.thrixel.com/remesh import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; import { Callout } from 'fumadocs-ui/components/callout'; Produce a new triangle count from an existing mesh - a [Detailer](/detailer) **or a [Sculptor](/sculptor)** result - without regenerating the texture. Fast. A Detailer pass is **not** a prerequisite: you can remesh a Sculptor output directly. To lighten a model you already have, remesh it rather than re-running the [Detailer](/detailer) at a lower `decimation_target`. Remesh reuses the existing geometry and texture, so it's much faster and cheaper - and you keep the same model, just lighter, instead of generating a new one. ## Re-decimate a mesh Requires API key. Two modes: - **Whole-mesh** (default) — leave `focus_on_node_names` empty and the new `decimation_target` is spread across every part of the model. - **Part-focused** — pass one or more part names in `focus_on_node_names` and the budget is applied only to those parts. Every other part keeps its current resolution, so you can lighten (or refine) a single component without touching the rest. **Whole-mesh remesh works directly on a Sculptor output** - you do not need to run the Detailer first. **Part-focused** remesh is the only exception: it needs per-part data, so it is only supported on **part-preserving** Detailer results (created with `preserve_parts: true`). A merged or Sculptor-based parent has no per-part data and returns `400` for part-focused mode. ### Request Body | Name | Type | Description | | --- | --- | --- | | `parent_submission_id` | string (required) | ID of a completed Detailer **or Sculptor** submission. Whole-mesh remesh accepts either; part-focused remesh requires a part-preserving Detailer. | | `decimation_target` | integer | Target triangle count. 1,000–200,000. Default: `200000`. Whole-mesh when `focus_on_node_names` is empty; otherwise the budget applied to each focused part. | | `remesh` | boolean | Run quad remeshing before decimation. Default: `true`. | | `focus_on_node_names` | string[] | Restrict the new budget to these named parts. Empty = whole mesh. Part-preserving detailer parents only. | | `project_id` | string | Assign to a project. | ### Example Request --- # Sculptor Source: https://docs.thrixel.com/sculptor import { Callout } from 'fumadocs-ui/components/callout'; **When to use Sculptor:** organic and photoreal forms - characters, creatures, props with soft or intricate surfaces. The output is a **single dense mesh**, not an editable part hierarchy, so it's not made for part-scoped [Edit](/edit). For structured, editable, multi-part objects use [Architect](/architect) instead. The Sculptor engine produces photorealistic, organic results from an image or text prompt. It takes a **single** input — either a text prompt or one reference image. ## Sculpt a model Requires API key. ### Request Body | Name | Type | Description | | --- | --- | --- | | `task` | string | Text description of the object to sculpt. | | `image` | string | Single reference image — base64 data URL or HTTPS URL. | | `images` | string[] | Reference image(s); the first is used. Mutually exclusive with `image`. | | `seed` | integer | RNG seed. 0-999,999. **Omit for a random seed each run** (the default). Pin it only to reproduce a previous result: the same seed with the same inputs reproduces it. | | `style_reference_submission_id` | string | UUID of a **completed** submission whose look (palette, materials, finish, level of wear) this one should match. Contributes appearance only - the subject still comes from `task` / `image`. Pass the same id across a set to keep it visually consistent. | | `project_id` | string | Group the submission under a project. | Provide either `task` or an image — Sculptor uses one input. `style_reference_submission_id` contributes **appearance only** - materials, palette, finish, how worn the object looks. The subject always comes from your `task` or your `image`; pointing at a wooden boat and asking for a barrel gives you a barrel, finished like that boat. It must be a **completed** submission of yours that still has its artefacts. Otherwise: `404` unknown id, `400` not completed or no artefacts, `403` it belongs to another account. Sculptor has no mesh prior, so with `task` alone the reference steers the image the mesh is built from. Supply an `image` **as well** and that image is restyled into the reference's look first - what you get back is no longer the picture you sent. For rules rather than looks - polycount budgets, naming, real-world sizes - attach a [project source](/organize) instead. Text states constraints well, a finished model shows appearance well, and they combine. ### Example Request ### Example Response ```json { "submission_id": "e5f6a7b8-...", "status": "queued", "created_at": "2026-04-20T14:33:05Z" } ``` ## Next steps Once the Sculptor job completes, chain any refinement step onto its `submission_id`. A Detailer pass is optional, not a prerequisite - you can [remesh](/remesh) a Sculptor output directly to a new triangle count, [generate textures](/detailer#generate-textures-for-an-existing-mesh) on it, or [detail](/detailer) it. `sculptor → remesh` is a valid path on its own. --- # Upload a mesh Source: https://docs.thrixel.com/upload import { Callout } from 'fumadocs-ui/components/callout'; Bring your own GLB file into Thrixel so you can run [Auto-fix](/autofix), [Edit](/edit), or [Detailer](/detailer) operations on it. Multipart form upload, max 100 MB. ## Upload an existing model Requires API key. ### Request Body | Name | Type | Description | | --- | --- | --- | | `glb_file` | file (required) | Binary `.glb` file (`Content-Type: multipart/form-data`). | | `task` | string | Optional label describing the uploaded model. Max 2000 chars. | | `project_id` | string | Assign to a project. | ### Example Request ### Example Response ```json { "submission_id": "d4e5f6a7-...", "status": "completed", "original_filename": "my_model.glb", "file_size_bytes": 4528192, "created_at": "2026-04-20T14:40:15Z" } ``` ---