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 | see below | Decimate to approximately this triangle count. |
--texture | off | Paint PBR textures as well as geometry. Needs the paint bundle, or the run is refused. |
--texture-resolution | recipe | Atlas edge: 1024, 2048 or 4096. Requires --texture. |
--matting | auto | Background removal before shape: auto keeps useful alpha, on recomputes, off keeps the pixels. |
--delight | off | Remove baked lighting and highlights after matting, before shape or paint. |
--front/--left/--back/--right | — | Named views for a 2mv reconstruction; any non-empty subset is accepted. |
mold run hunyuan3d-2.1 --image chair.png --texture --texture-resolution 2048 --matting on --delight -o chair.glb
mold run hunyuan3d-2mv-turbo --front front.png --left left.png --right right.png -o object.glb--target-faces behaves differently depending on whether you asked for a texture. A geometry-only export keeps the full-density surface, which is what you want for printing. A textured render decimates to 40,000 triangles when you name no value, because that is what Tencent's own paint pipeline does before it unwraps UVs — and UV unwrapping is superlinear in triangle count, so an undecimated shape can take minutes to over an hour on one CPU core. Name a value and it is used as-is, in both directions; the host advertises the budget it will apply as capabilities.mesh.target_faces_texture_default, and every authoring surface shows it in the field's placeholder.
--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), mold library export, 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.glbDurable workflows from the CLI
mold run against a 3-D model is one render: it succeeds or it does not. mold mesh-workflow is the durable form of the same work. Each stage — the picture a text-to-3-D run starts from, its matted and delighted copies, the shape, the paint — is admitted as its own generation and keeps its own retained artifact, the job survives a server restart, and a resume picks up at the first unfinished stage rather than starting over.
mold mesh-workflow create --prompt "a small ceramic fox" --texture --follow
mold mesh-workflow create --mesh chair.glb --image chair-albedo.png
mold mesh-workflow create --mesh chair.glb --mode mesh_roundtrip --octree 320
mold mesh-workflow list
mold mesh-workflow show WORKFLOW-IDThe mode is inferred from what you supply: --prompt renders a picture and reconstructs it, --mesh with --image paints the mesh you supplied, and --mesh alone rebuilds it through the 2.1 shape VAE. --mode names it outright when a script would rather not depend on that.
Every verb talks to the server at MOLD_HOST. A workflow is durable on ONE machine — its manifest, its stage artifacts and its queue rows live in that machine's data root — so there is no local form, and --local is refused with the one-shot alternative rather than quietly running something else.
The geometry and conditioning controls are the same ones mold run takes: --octree, --threshold (also accepted as --mesh-threshold), --target-faces, --matting, --delight, --texture and --texture-resolution. This is the only surface that exposes all of them on a durable workflow; the apps author texture, texture resolution and delight only. --matting and --delight prepare a conditioning picture, so a roundtrip refuses them by name rather than taking a control its stages never reach. --seed applies to every stage, so one value reproduces the whole run.
Follow a running workflow with --follow on create, or attach to one later with mold mesh-workflow events. Either prints a line each time a stage changes state; the granularity is the server's own polling interval, because the event stream sends whole snapshots rather than per-step progress. resume restarts a paused or failed job, cancel stops a running one, and delete removes a settled workflow and the artifacts it retained — settled only, so cancel or wait first.
From the apps
The web SPA, the desktop app, and the iPhone app all generate and view meshes now — not only the CLI and Discord.
The apps use ordinary 3-D generation in Create. To start from text, generate an image first, then attach it as the source for a Hunyuan3D render. Dedicated 3-D Studio authoring has been retired from the apps. Durable multi-stage mesh workflows remain available through the CLI and API described above; their published outputs appear individually in the Library.
The native macOS app can view and export existing meshes, but does not yet support Hunyuan3D authoring in Generate.
- 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 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. Library grids 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.
