3D Meshes
mold generates 3-D meshes from a single photograph using Hunyuan3D. The result is a durable library print like any other — it lands in the gallery, gets a poster tile, and lists, downloads, trashes, restores and reuses its settings exactly like an image or a clip.
Quick start
mold licenses # read the terms
mold pull hunyuan3d-mini-turbo --accept-license tencent-hunyuan3d-2.0
mold run hunyuan3d-mini-turbo --image chair.png -o chair.glb
mold run hunyuan3d-2mv-turbo --front front.png --left left.png --back back.png -o chair.glbmold pull refuses until the licence is accepted. That is deliberate: the Tencent community licence does not apply in the EU, the UK or South Korea, so mold will not acquire the weights on your behalf without an explicit acceptance. See Licence.
What makes a good input
There is no text encoder in this family. The image is the entire conditioning, and the prompt — if you pass one — is recorded as provenance and never read. You do not need to write one: every surface reads the prompt requirement off the model's own generation profile, so an empty prompt is admitted here and refused everywhere it still means something.
mold run hunyuan3d-mini-turbo --image chair.png -o chair.glb # no promptThe usual prompt-engineering advice does not apply, and the image advice matters more than usual:
- One object, centred, filling most of the frame.
- A plain or removed background. An image with an alpha channel is the best input; mold letterboxes on the cutout.
- A three-quarter view, not a straight-on one.
A request without a source image is refused rather than answered from nothing.
Prompt expansion follows the same rule. mold expand, mold remix, the Expand and Remix controls on every surface, and the MCP expand_prompt / remix_prompt tools do not call a language model for this family: the one answer is the guide's advice above on preparing the image, and --expand on a mesh run is skipped rather than rewriting the recorded prompt.
The same picture also meshes differently here than in ComfyUI. mold prepares the image the way Tencent's ImageProcessorV2 does — crop to the alpha bounding box, then letterbox on a white square, so nothing is cut away — while ComfyUI's clip_preprocess drops the alpha channel and, with CLIP Vision Encode's default crop: center, centre-crops the shorter side to a square (crop: none squashes to a square instead, distorting rather than cropping). An off-centre or wide subject loses its edges there and keeps them here.
Controls
mold run hunyuan3d --image chair.png \
--octree 320 \
--mesh-threshold 0.55 \
--target-faces 50000 \
-o chair.glb| Flag | Default | What it does |
|---|---|---|
--octree | 256 | Query-grid resolution. The detail knob; cost is cubic. |
--mesh-threshold | 0.6 | Iso-level. Lower recovers thin features and adds noise. |
--target-faces | none | Decimate to approximately this triangle count. |
--mesh-threshold is a level on the same [0, 1] occupancy scale ComfyUI's VoxelToMesh node uses, so a value tuned there carries over unchanged.
--octree accepts 128, 192, 256, 320 or 384 — an allowlist, not a range, because the model evaluates its occupancy field on (n + 1)³ points and an arbitrary value between two rungs buys nothing while risking an out-of-memory failure part-way through a render.
Flags that describe a raster or a timeline are refused, not ignored: --frames, --fps, a mask, a ControlNet and an explicit canvas all name something a mesh does not have, and silently dropping them would make "Reuse settings" replay numbers that never applied.
Output format
The stored artifact is always binary glTF (.glb) — one self-contained file with geometry, normals and materials embedded. That is what makes a mesh a single library row, with no special-case handling anywhere downstream.
You do not have to ask for it. A 3-D model has exactly one deliverable container, so a request naming png is pinned to glb rather than refused — an older client that always sends a raster format still gets its mesh. -o is the one place that is an error instead: a filename ending in .png, .mp4 or .wav names a file this render will not write, and mold says so before a weight is read rather than after a two-minute render.
Export as OBJ, OBJ+PBR ZIP, STL or PLY
Everything except GLB is an export: a transcode of geometry that already exists, never a generation target, because each container loses something the stored glTF carries.
mold library export chair.glb --format stl # writes chair.stl
mold library export chair.glb --format obj -o ~/chair.obj
mold library export chair.glb --format ply --output - # to stdout| Format | Carries | Reach for it when |
|---|---|---|
glb | Geometry, normals, UVs, materials, embedded texture | Anything. This is the stored file. |
obj | Positions, normals, UVs. No materials. | Blender, MeshLab, most DCC importers. |
zip | OBJ, MTL, and exact embedded PBR PNG maps. | Portable painted-asset handoff. |
stl | Triangles and one normal each. No UVs, no colour. | 3-D printing and CAD. |
ply | Positions and per-vertex normals, vertices shared. | Point-and-mesh tooling, research code. |
The gallery file is never renamed or replaced — an export writes a copy where you asked for it. The same conversions are available on every surface: the API (POST /api/gallery/export/:filename), the TUI's export picker, the export_mesh MCP tool, and the apps' Export as… menu. A host advertises what it can convert on /api/capabilities.mesh.export_formats.
USDZ is tracked separately; it is the format Apple's AR Quick Look wants and it carries textures, so it belongs with the texturing work rather than here.
Print-ready exports
The stored GLB is normalized model space — Hunyuan3D fits every mesh into a unit cube — so a slicer reads it as a 2 mm blob and refuses it ("object too small… may be in meters or inches"). Blender's STL and PLY importers take axes exactly as they come, so the mesh lands on its side, while Blender's OBJ importer converts Y-up itself and DCC tools already treat one unit as one metre. --size-mm, --up-axis and --origin on obj, stl and ply fix that at export time, each defaulting to what that format's tools expect:
| Format | Size | Up axis | Origin |
|---|---|---|---|
obj | as stored (model units, no scaling) | y | floor |
stl | 100 mm | z | floor |
ply | 100 mm | z | floor |
floor centres the mesh on the up-axis plane and rests it on 0 — bed-ready for a slicer. center puts the bounding-box centre at the origin instead; reach for it when the tool on the other end places objects by their centre. The stored mesh is centred on Hunyuan3D's extraction grid, not on its own bounding box, so center genuinely recentres it.
mold library export chair.glb --format stl # 100 mm, Z-up, floor
mold library export chair.glb --format stl --size-mm 120
mold library export chair.glb --format obj --up-axis y --origin centerA key you leave out takes the table above. --size-mm (1 to 1000), --up-axis (y | z) and --origin (center | floor) are refused, not silently dropped, on glb and on a turntable, and both mold library export and the MCP export_mesh tool refuse them outright against a host that does not advertise capabilities.mesh.export_geometry.
Share a turntable
Nothing outside a 3-D tool opens a .glb, and the gallery poster shows one view. A turntable is that poster set spinning: the same camera, lighting and slate background, swept a full turn around the mesh and written as an animated GIF, APNG or WebP you can drop into a chat, a README or a browser. The sweep's scale is fitted once, before the first frame renders, to the mesh's own rotation-invariant extent — so the mesh holds one size for the whole turn instead of breathing and popping as it comes round, and a 36- or 72-frame turntable frames identically. Frame 0 is the poster's exact pixels: poster, turntable, and the interactive 3-D viewer's home view all share that one camera and framing. The sweep turns the mesh the same direction a rightward drag turns it in the viewer, and the viewer's "reset view" returns to that same poster view.
mold library export chair.glb --format gif # chair.gif: 36 frames, 10 fps, 512 px, loops
mold library export chair.glb --format gif --playback bounce --repeat once
mold library export chair.glb --format webp --frames 72 --fps 24 --max-dimension 768
mold library export chair.glb --format apng -o chair-turntable.png
mold library export chair.glb --format apng --transparent # no backdrop| Flag | Values | Default | Meaning |
|---|---|---|---|
--playback | loop, bounce | loop | GIF only. loop is one seamless full turn; bounce sweeps half a turn and plays it back. |
--repeat | forever, once | forever | GIF only. once plays through and rests on the final frame. |
--max-dimension | 240 to 2048 | 512 | Frame edge in pixels; frames are square like the poster. |
--frames | 8 to 180 | 36 | Views rendered around the mesh. 36 is a 10° step; 72 is smoother and twice the size. |
--fps | 1 to 30 | 10 | Playback rate. 36 frames at 10 fps is a 3.6 s turn. |
--transparent | flag | off | Render the object over nothing instead of the slate backdrop. |
The two sweeps are shaped for how the encoders play them back. A loop renders one full turn whose last frame stops one step short of the first, so the wrap from last to first is a step like any other rather than the poster held twice. A bounce renders half a turn, first frame to last inclusive; the GIF encoder appends the interior frames in reverse, so the animation swings out to the far side and back, and the reversal reads as deliberate instead of a full turn snapping into reverse the moment it comes round. Bounce and --repeat once are GIF contracts — APNG and WebP always loop — exactly as they are for a video export. A turntable is a render, not the mesh: it carries no geometry, and the flags are refused on a geometry format rather than ignored.
--transparent leaves the backdrop out so the turn can be dropped onto a slide, a README, or any page that is not slate blue. APNG and WebP carry the full alpha channel, so the object keeps the soft outline it was rendered with; a GIF has one transparent colour rather than a channel, so its outline is a hard cut. Transparency belongs to a turntable alone — a geometry container has no backdrop and a video's frames already exist, so the option is refused on both rather than ignored.
The same options are on POST /api/gallery/export/:filename (playback, repeat, max_dimension, frames, fps, transparent — the video export's own field names plus the one only a render has), the export_mesh MCP tool, and the apps: the web, desktop and iPhone export menu shows an Export turntable… entry whenever the host advertises an animated container, and it opens the same options sheet a video export uses, with a Background · Transparent checkbox that is remembered for the next export. A host lists gif, apng and — on a build with the webp feature — webp in capabilities.mesh.export_formats beside the geometry containers, so a client learns what it can ask for without trying. Rendering is pure CPU on the serving host; 36 frames at 512 px take well under a second, and the frame buffer is capped at the same 256 MiB the video export allows, so 180 frames at the largest size is a 422 naming the two flags that bring it under.
Piping
mold run is pipe-friendly here as everywhere:
mold run hunyuan3d-mini-turbo --image chair.png --output - > chair.glb
cat chair.png | mold run hunyuan3d-mini-turbo --image - -o chair.glbFrom the apps
The web SPA, the desktop app, and the iPhone app all generate and view meshes now — not only the CLI, TUI, and Discord.
Web and desktop include a dedicated 3-D Studio at /create/3d. Text to 3-D first renders a source image with any installed image model, then removes its background, optionally removes baked lighting and highlights, and builds the Hunyuan3D mesh; it can optionally paint PBR materials. Texture a mesh accepts GLB or OBJ plus an appearance image and publishes a textured GLB. These are durable server workflows: image, matting, delight, shape, paint, and finalization are checkpointed, a shutdown parks unfinished work, and Resume continues the same child job after restart. Settled workflow-owned inputs and stage copies remain available until Delete workflow data releases them; deleting the Library print remains a separate action. On the desktop app the settings rail carries Settings | Recent. Recent lists that machine's past workflows — what each one made, what stage it is on or how it ended, and how long ago — and one click brings its whole recipe back: the mode, both styles and every stage setting. A supplied mesh cannot come back (a file is not something an app can re-open for you), so Rebuild and Add texture say which file to choose again. New workflow on the toolbar starts fresh without forgetting the machine or the styles you are using.
Choose Run workflow on before authoring to bind the whole studio session to one connected machine. Its models, previous workflows, uploads, progress, resume/cancel actions, and final GLB all stay on that machine; authenticated hosts receive their saved API key. Switching machines opens that machine's own workflow state rather than carrying a draft or job identity across hosts.
- Pick a Hunyuan3D model in Create. The form reshapes itself from the model's own generation profile: Shape, Resolution, exact-size, Fit to canvas, Strength, Mask, and Negative all disappear, because the profile is canvasless, strengthless, maskless, and reads none of them.
- Attach a source image. It is still the only conditioning, so Generate stays disabled without one. The prompt field becomes an optional note — its placeholder explains the model has no text encoder and renders from the image alone — and Generate is enabled with an empty prompt.
- Set the Mesh controls that took the raster controls' place: Octree detail over the advertised allowlist (128/192/256/320/384, 256 default), an Iso threshold slider, and an optional Target faces field within the advertised bounds — leave it blank to keep the raw surface. The request sent to the server carries only the values that differ from the advertised defaults. On desktop, a CUDA host that advertises Hunyuan3D Paint also offers PBR materials and the host's 1024/2048/4096 texture atlas choices. Enabling it paints base color, metallic/roughness and normal detail into the stored GLB after geometry completes. A host that advertises Delight also offers Remove lighting and highlights. It runs the fixed 512px Tencent pipeline after background removal and before shape or paint.
- Generate. The result renders in the same WebGL viewer the Library uses, right in the Create result area: it auto-rotates until you touch it, honours
prefers-reduced-motion(and parks or resumes the moment that setting changes), and has fullscreen and wireframe toggles, captionedtris · verts · bounds. Auto-rotate and fullscreen belong to the Create result area only — the Lightbox and the viewer sheets open the mesh still and framed — and fullscreen is unavailable inside the iOS WKWebView, where the button never appears. The wireframe toggle is disabled, with a reason, for a mesh with no edges to outline. - Find it again in the Library through the 3D badge on the tile, or on web and desktop through the 3D chip in the kind filter (alongside Images, Video, and Audio); the iPhone Library has the badge only. Reuse settings restores the octree, threshold, and target faces recorded on the print rather than a form's leftovers, and Use as source is refused for a mesh — there is no raster to stage as conditioning.
- Export from the lightbox (web/desktop) or the viewer sheet (iPhone). An Export as… entry offers whatever the host advertises on
capabilities.mesh.export_formats— never the stored GLB itself, which Download already covers: OBJ, ZIP, STL and PLY open a print-ready options sheet — size in millimetres with a live width × depth × height readout once the mesh is loaded, up axis, and origin, pre-filled fromcapabilities.mesh.export_geometry's per-format defaults described in Print-ready exports above — and Export turntable… opens the video export's options sheet for the animated GIF, APNG or WebP described in Share a turntable. Desktop saves through its normal download path; on iPhone and Android every entry — the stored GLB too — offers both Share… (the native share sheet) and Save to Mold folder, which files it under Files ▸ On My iPhone ▸ Mold (the entry appears after the first save) or Android'sDownload/Moldand names the saved path. On iPhone the size, axis, and origin fields sit inside the swipe-up sheet under the format picker, never floating over the viewport.
In the TUI
Pick a Hunyuan3D model in mold tui's Create form and the form reshapes itself from the model's generation profile rather than from its name:
- Source image is the conditioning row for ordinary single-view models. A multiview recipe also shows Named views, edited as semantic
front=/a.png; left=/b.jpg; back=/c.png; right=/d.pngslots. Any non-empty subset is accepted, the TUI orders it front/left/back/right before sending, and choosing one form clears the other. Strength, Mask and the Negative prompt disappear because the profile advertises no strength (supports_strengthis false), a hidden mask, and no negative prompt. - Advanced ▸ 3-D mesh appears with four rows — Octree (
◀▶walks the advertised allowlist), Iso threshold (0.05 per press inside the advertised range) and Target faces (10 000 per press; stepping below the minimum turns decimation off), and Remove background (Auto, On, or Off from the advertised profile). Auto preserves useful supplied alpha and runs the pinned U²-Net pre-stage for opaque inputs. Each row readsdefaultuntil touched, showing the profile's own default, and an untouched row sends nothing so the recipe's defaults apply. - Format is pinned to
glb;◀▶cannot walk it onto a raster container the server would only pin straight back. - Generate submits with an empty prompt, because the profile advertises
prompt.mode: ignored; the same gate still refuses an empty prompt on a text model.
A finished mesh saves mold-<model>-<timestamp>.glb beside your other prints, caches its poster where the Library looks for thumbnails, shows the poster in the Preview panel, and captions it with 49,152 tris · 24,576 verts · 1.00×0.80×0.60.
In the Library, a .glb tile shows its poster (fetched from the owning machine's thumbnail route; never the geometry through a raster decoder), and x opens an export picker offering OBJ, ZIP, STL, PLY and the turntable formats (GIF, APNG, and WebP on a build that encodes it) — the list the owning machine advertises on capabilities.mesh.export_formats, or the same set from the in-process writer for a print that lives only on this machine, rendered through the same code the server uses. The picker has no knobs: a turntable renders at the defaults (one full turn, 36 frames, 512 px, 10 fps, looping) and OBJ/ZIP/STL/PLY export at their own per-format defaults (the Print-ready exports table above) — its hint says so, pointing at mold library export for bounce, once, a different size, or a different axis and origin. A local export and the equivalent served one apply the identical defaults, so the bytes are the same either way. The converted copy is written beside your other saves as <print>.<ext> (an APNG as .png) and its path is shown when it lands; the gallery file is untouched.
In Discord
/mesh renders a Hunyuan3D mesh. Attach source for a single-view model, or attach any non-empty subset of front, left, back, and right for a 2mv model. When model is omitted, single-view input selects the ordinary default and named views select hunyuan3d-2mv-turbo:fp16. The command also exposes seed, texture, matting, octree, threshold, and target-face controls. /generate with a Hunyuan3D model and source_image remains available for compatibility. Its prompt option is optional whenever a source image is attached (Discord cannot make an option optional per model, and a source image is exactly what image-to-video and image-to-3D have in common), the video_format option is ignored because the family has one deliverable container, and the reply embeds the rendered poster with the .glb attached beside it as a download. The summary reads Mesh Generated with the triangle and vertex counts, the bounds, the format and the seed. A mesh larger than Discord's upload limit posts the poster alone with a note saying to fetch the .glb from the gallery.
In the gallery
A mesh cannot be decoded by a raster thumbnailer, so mold renders a poster PNG at save time and stores it in the shared thumbnail cache. Grids and the TUI cell show that poster; only the lightbox loads the geometry itself. If a poster is missing, surfaces fall back to a placeholder rather than trying to draw glTF bytes as a picture.
Text-to-3D, supplied-mesh retexturing, named multiview input, background matting, and delight preprocessing are available through the server-backed 3-D workflow. PBR painting is available on builds that include mesh-texture; delight is available when the host includes mesh-delight. A host without the paint feature keeps Text to 3-D available for geometry and hides the PBR toggle instead of submitting a request that the host must reject. Official signed macOS desktop builds include the complete mesh feature set.
