Image to 3D

Updated Oct 14, 2026 Open the tool

Image to 3D generates a textured 3D model from a picture using third-party AI providers (Meshy and Tripo), then hands the model to the YDR editor so it can be exported as a prop, weapon or clothing. Credits are charged up front and refunded automatically when a generation fails.

The /app entry (2026-09-13)

The Create tools in the /app workspace have their own door to the same engine: a Generate model button on the Props, Weapons and Clothing panels that swaps the panel for a form (an image or a description, the branded model, the detail slider with the same honest warnings, textures on or off, the cost before pressing) and calls these same v1 routes. What differs from the legacy pages: the finished GLB lands in the person's workspace library as a row of its own (copied out of ai_assets once the files are persisted, by the webhook, the Tripo sync, or the next read of the job, whichever comes first), the run shows in the workspace tray and under Generations in the right column, and Use this model loads it into the Create tool in hand, where converting it is free. The v1 route writes a ws_jobs row per task and answers with wsJobId; see workspace.md, "Generate model". The legacy /app/prop?generate=1 page keeps working until it is redirected.

Where Image to 3D lives

Image to 3D runs at /app/prop?generate=1 ("Image to 3D Model": "Turn a picture into a 3D model. Generate from an image, then open it in the editor to export as a prop, weapon or clothing."). The same "Image" and "Text" tabs appear on the Prop, Weapon and Clothing creators. The "Text" tab first generates a reference image and then runs the image flow ("Describe what you want to make. We generate a reference image, then you turn it into a 3D model.").

The older panel inside the legacy YDR editor (/create/ydr?panel=imageto3d, which now redirects to /app/prop?generate=1) was titled "Image to 3D Model". It names the models "Imagine V1" and "Imagine V2" and adds a "Priority Generation" option. Discord notifications used to link to that panel; since 2026-09-23 they open /app/prop instead.

The Imagine models

Image to 3D offers four branded models in the picker:

  • Imagine: "Fast and reliable for most props". Provider Meshy, ai_model: latest. Default 30,000 polygons.
  • Imagine Detail: "Sharper surfaces on complex subjects". Meshy meshy-7. Default 30,000.
  • Imagine FAST: "Results in a fraction of the time". Meshy meshy-5, badge "New". Default 30,000.
  • Imagine Pro (formerly Imagine Pro; the id imagine-v2 stays in stored settings): "Highest detail with PBR textures". Provider Tripo, marked "Pro". Default 20,000. Up to 20,000 polygons it uses Tripo's P series (P1-20260311, clean game topology); above that the H series (v3.1-20260211).

Imagine Pro needs a Pro or Max plan. The server rejects other plans with "Imagine Pro needs a Pro or Max plan." and the button reads "Upgrade for Imagine Pro". In the older panel "Imagine V1" is Meshy and "Imagine V2" is Tripo P1 ("Available with Pro and Max plans."). A model can also be marked "Unavailable" when the provider account behind it cannot run it; see "When a model is unavailable" below. Everything a person reads names the branded model, never the provider's own id (meshy-7, P1-20260311): generationModelLabel in src/lib/app/create/generation-models.ts maps a provider plus its model id to the branded name for error messages and Discord entries.

When a model is unavailable (2026-09-23)

A model is unavailable when our own account at the provider behind it cannot run it: fewer than 50 provider credits, a missing key, or a balance lookup that fails or times out. It is never about the person's plan or their credit balance, and nothing is charged.

Since 2026-09-23 the /app generator says so before any work is done. The picker reads GET /api/v2/mesh/availability when it opens (at most once a minute) and marks any model the server calls unavailable with an Unavailable badge in place of its usual badge, plus a crossed-circle mark on the engine row when every version of that engine is out. The model can still be picked and read about; what changes is the footer, which reads "{model} is unavailable right now" with Generate disabled, instead of the usual cost line. Before this the person chose an image, pressed Generate and met a 503. If a generation does come back 503, the picker re-reads the state at once, so the next press already shows the badge.

The route that starts a generation makes the same check and is still the one that decides. Its refusal is 503 with code: 'provider_unavailable' and the sentence "{model} is unavailable right now. Pick another model, or try again in a few minutes.", which names the branded model (Imagine, Imagine Detail, Imagine FAST, Imagine Pro) rather than "this service", so it is clear another model may work. Meshy powers Imagine, Imagine Detail and Imagine FAST, so those three go out together; Imagine Pro is Tripo and goes out on its own.

The availability answer carries only a true or false per model and the time it was checked, never a balance. A check that fails or is blocked leaves every model usable, because the server refuses anyway and an unreachable check must not hide a model that works.

What the 3D generator says when it refuses

Meshy and Tripo refuse a picture in their own wording, at two moments: when the task is created, and when a task that was accepted comes back FAILED. Both used to reach the person as they came ("Failed to start generation: moderation_blocked"), so a picture that could be fixed read like an outage. Since 2026-09-23 the refusal is sorted into one cause and each cause has one sentence (src/lib/app/server/generation-refusals.ts, shared with the image tools, see the Credits doc):

  • Content: "That image was rejected by the 3D generator for content reasons. Try a different picture."
  • The picture itself (no clear subject, unreadable, wrong size): "The 3D generator could not read a model from that image. Use a single subject, in focus, on a plain background."
  • Us, not the user (no provider credit, rate limit, provider outage): "The 3D generator is unavailable right now. Try again in a few minutes."

A refusal nobody recognises keeps the provider's own words, because a wrong guess is worse than a strange one. A Tripo task whose status comes back banned is always reported as a content rejection: that status carries no message of its own. The refund sentence follows the cause either way, so the user is told both why it stopped and what happened to their credits.

Polycount slider and the honest warnings

Image to 3D has a "Target polygons" slider with the captions "Lower · lighter file" and "Higher · more detail, slower". For the Meshy models the range is 2,000 to 50,000 in steps of 1,000 (default 30,000). For Imagine Pro it is 2,000 to 100,000 (default 20,000), with the note "Up to 20k polys: clean, game-ready topology (P series)." or "Above 20k polys: high-detail geometry (H series)."

Two warnings appear depending on the value, and both are honest about trade-offs. Below 10,000: "Heads up: low counts can rip or tear the surface" and "thin parts, logos and text are the first to go. The default is the safer bet; you can always remesh lower in the editor." Above the model's default: "Heads up: very high counts can add surface artifacts and slow generation. The default is tuned for most props." Never tell a user that lower is cleaner; it is not.

Every generation card also says: "AI does its best" then "most models come out great, but fine details like logos, text and thin parts often need a touch-up in the editor. If a generation fails, your credits are refunded automatically." The older panel additionally scans the image and warns "This image may produce unexpected results." for logos, flat graphics and text, adding "Use transparent backgrounds."

Made for a tool: the pose and the framing (2026-10-13)

Every tool that hosts the generator says what the model is for, and the form shows it under From an image · Describe it as a row with its label (and a picture when there is one). A tool with a pose picture shows the row on both sources; one without (every tool but Animals) only changes how the words are drawn, so its row shows on Describe it only (since 2026-10-13), and a photo on From an image is sent as it is. With a tool's context, the words go to the image route as a picture made as a reference for a 3D model (category model-subject with the tool's own direction), never as the inventory-icon picture the generator drew before. The directions are in src/lib/workspace/modes/convert/generate/contexts.ts (Animals and Shells keep theirs in their own tools).

  • Animals (/app/animal, the bar under the animal or Generate with AI in the Model step): "Posed like the Retriever" with a grey clay figure of that game animal in its skeleton's stance. Describe it: the words become a picture made as a reference for a 3D model, of that subject standing exactly as the clay figure stands (all four legs straight down at shoulder and hip width, head level and in line with the body rather than turned to the viewer, tail out behind; birds with wings spread, sea animals swimming level, monkeys with arms out), so the generated model lands close to the game animal's bones before a point is moved. From an image gains Redraw in this pose (on by default): the person's photo (a picture of their own dog, say) is first redrawn in that pose, keeping who it is (breed, colours, markings, face), then the model is made from the redrawn picture. Off, the photo is used as it is. Redrawing costs one picture generation, shown in the price line ("83 credits (75 + 8 for the image)"); the pose picture is counted in it.
  • Shells (/app/interior, Generate beside Add your own (.glb) in the Furnish step): "Furniture for the shell": the picture is made as one piece of furniture standing on its base, front three-quarter, nothing around it. The finished GLB joins the shell's own pieces exactly like one added from a file.
  • Props, Weapons, Clothing (the generator in their own column, Generate model, and the column itself with nothing loaded; since 2026-10-13): "Made for Props", "Made for Weapons", "Made for Clothing". Props: one object standing upright on its base, real proportions, front three-quarter from slightly above, nothing around it. Weapons: one weapon seen exactly from its right side, muzzle pointing right, no hands, no background. Clothing: one garment on its own, front view, laid out as worn on an invisible body with sleeves and legs straight, no person, mannequin or hanger. The context is on for as long as the tool is open, and Use model puts the result in hand as before.
  • Weapons attachments (/app/weapon with a weapon in hand, Generate beside Add attachment in Parts, a menu of the attachments still free; since 2026-10-13): "Magazine for the weapon" or "Suppressor for the weapon", in a dialog titled "Generate a magazine" or "Generate a suppressor": the part alone, seen exactly from its side with its front (the suppressor's muzzle end) pointing right, no gun, no hands. The finished GLB becomes that attachment exactly like a GLB picked from Add attachment ("Magazine added"). The dialog's context sits over the Weapons column's own while it is open; closing it puts the weapon's back.
  • Peds (/app/ped, Generate with AI in the Add question, and since 2026-10-13 the bar under the ped while nothing has been added yet, "Clothing for the ped" with Choose a GLB and Generate with AI): the picture is made as one clothing piece shaped for a human body, front view, as worn with the arms straight down and slightly apart, no person, mannequin or hanger. The finished GLB is added as a piece, as a generation always was.
  • Chains (/app/chain, Generate under the pendant ways in the Add tab; since 2026-10-13): "Pendant for the chain", dialog "Generate a pendant": one pendant facing the camera in a flat front view, its bail or loop at the top, no chain, neck or person. The finished GLB is added as a model pendant, exactly like a GLB from SVG, GLB, OBJ.
  • Preview (/app/preview, Generate with AI beside Choose file on the empty stage; the browser tool only, never in the Photoshop or Photopea panel; since 2026-10-13): "Made for Preview", dialog "Generate a model": one object on its own, whole and centred, front three-quarter, nothing around it. The finished GLB opens in Preview like a dropped file.

Whose result is it (2026-10-13). The generator keeps every tool's context in force as a stack (setContext in generate/job.svelte.ts, stackContexts in contexts.ts): the latest on top, one taken off by its key wherever it is. A run records the context it was started with (runContext), and a tool's dialog (GenerateDialog.svelte) takes back only a run started with its own context's key; any other run goes where it would have gone without the dialog. So in Weapons a model generated in the column for the weapon itself still opens as the weapon, an attachment generated in the dialog still becomes that attachment (also when it finishes after the dialog was closed, or with the other attachment's dialog open), and a run started in Props, Weapons or Clothing and used from the tray while Animals, Shells, Chains or Preview is open opens in the Create tool last used, not in that tool (Peds still takes any generation finished while it is open as a piece). The description and pictures in the form are shared by every place the generator opens in a tab, as before: words typed for a weapon are still in the field when the magazine dialog opens.

Credit cost per model

Image to 3D charges when the task is queued. Textures are on by default in the new intake, so the everyday prices are:

  • Imagine, Imagine Detail, Imagine FAST (Meshy): 130 credits (105 base + 25 texture). With the tools.convert permission (Referrer, Standard, Pro, Max): 75 credits (50 + 25).
  • Imagine Pro (Tripo): 55 credits (35 base + 20 texture) for Pro and Max, the only plans allowed to use it.
  • Priority Generation (older panel, Meshy only): +40 credits, or +30 with tools.convert.
  • Text tab reference image: a GPT Image medium 1:1 render, 25 credits on Free, 12 on Pro. Lite and Booster pay Free's 25 (since 2026-09-30; they paid 20 before). Standard pays its own 15 even though the tab quotes the Free or Pro figure (verify).
  • Remesh in the editor: the paid AI remesh (60 credits, 50 with tools.convert) belonged to the legacy editor and is gone; its route was removed on 2026-10-13. In the workspace, "Reduce the mesh" lowers the triangle count on your own machine and costs nothing.

Pro users see the Free price crossed out; others see "Save {n}% on every generation" with "Pro pays {n} instead of {n} per render." If the balance is short the button says "Not enough credits" and "You need {n} more credits to generate." appears with credit packs. The server also refuses with "Insufficient credits. You need {n} credits." Reopening a finished model from "Recent generations" is free ("Reuse for free").

Limits before a task starts

Both providers read only JPEG and PNG; Tripo refuses a file over 20MB and suggests at least 256 px per side. Meshy publishes no size limit. Our own hard limit is lower than either: a Vercel function takes a request body of about 4.5MB, and anything larger is answered by the platform with a bare 413 before our route runs.

In /app (since 2026-09-22) each picture is made ready in the browser before anything is charged: the picker takes up to 50MB per file (PNG, JPG or WebP), each view is cut to 2048 px on its long side and written as PNG (JPEG stays JPEG), and it is uploaded straight to storage, so the size of the picture no longer matters to the function. Messages: "{name} is {w}×{h} px · needs at least 256 px per side", "{name} could not be opened · save it as PNG or JPG", "{name} is over 20 MB even at 2048 px · save it as JPG" (only for extreme noise), "Could not upload {name} · check your connection". A bare 413, 502, 503 or 504 from the platform now reads as a sentence ("The images are too large to send…", "…took too long to answer…") instead of "(HTTP 413)".

The legacy pages still send data URLs inline; the route accepts them (PNG, JPG or WebP, 25MB each by its own check, "Source image exceeds 25MB limit"), but in practice anything over about 3MB of picture fails at the platform's 4.5MB cap with a 413. Texture prompts are limited to 600 characters.

Queue limits: Meshy allows 10 tasks in PENDING or PROCESSING at once, counted across all users; Tripo allows 20 per user. Over the limit the UI says "Queue full ({n}/{limit})." and the server "Queue limit reached. Please wait for some to complete before generating new assets." If the provider account holds fewer than 50 provider credits, or its balance cannot be read at all, that provider is reported unavailable and no one is charged: the preflight sets serviceAvailable: false (the legacy pages then say "Service temporarily unavailable.") and the generation route answers 503 with the branded sentence "{model} is unavailable right now. Pick another model, or try again in a few minutes." Accounts under a verification restriction are refused with the verification message.

How long it takes and how you are told

Image to 3D usually takes a few minutes. The progress card says "Generation can take a few minutes. We'll send you a Discord notification when it's ready." and steps through "Initializing…", "Generating geometry… {n}%", "Refining mesh…", "Generating textures…" and "Finalizing…". The server code describes the Meshy path as 2 to 10 minutes.

When a Meshy task succeeds the bot DMs the user "Your 3D model generation is complete!" with an embed titled "Model Ready!", reading "Your model is ready and saved to your library." and linking to the workspace's Props tool at /app/prop. A remesh sends "Remesh Ready!" and "Your remeshed model is ready and saved to your library." with the same link. Both used to point at /app/convert?to=ydr, which since the workspace went live redirects to the Converter's YDR recipe, so the DM opened the wrong tool.

When it fails the DM is "Your 3D model generation failed." with "Generation Failed", the reason, the refund line and "Please try again with a different image, or contact support if this keeps happening." The reason is the refusal sentence for the cause when there is one, the same sentence /app shows, so a picture refused on content says so in the DM too. Tripo (Imagine Pro) results are polled by the page and logged to the team channel; no user DM is sent for Tripo (verify).

Automatic refunds and how duplicates are prevented

Image to 3D refunds the full charge whenever the user does not get a model. If the provider rejects the request or the task cannot be saved, the refund happens immediately. The error is the refusal sentence for the cause when there is one ("That image was rejected by the 3D generator for content reasons. Try a different picture."), and otherwise "3D generation could not be started: {reason}" with the provider's own words; either way it is followed by "Your {n} credits have been refunded" or, if the refund itself failed, "We could not automatically refund your {n} credits; our team has been alerted".

If a Meshy task fails later, the Meshy webhook refunds it (reason Refund for failed Meshy task {id}). Since 2026-10-13 the site confirms every webhook with Meshy before it refunds or marks a model ready: it reads the task back from Meshy and acts on that answer. Failed generations are still refunded the same way. A task reported as succeeded without a usable model file is treated as failed: "Generation completed but no usable model file was produced." The status endpoint has a fallback refund for the case where the webhook never arrives.

A succeeded task whose model could not be downloaded is not a failure on its own (2026-10-12). Meshy sends the SUCCEEDED webhook several times, and the early deliveries can carry assets.meshy.cn links that time out from our servers while a later delivery's assets.meshy.ai link works. Until 2026-10-12 the first failed download failed the workspace job for good with "Generation completed but no usable model file was produced." and no refund, and the model the later delivery stored never reached the library: 29 runs, 23 people, 2,505 credits between 2026-09-15 and 2026-10-11, every one with model.glb in ai_assets. Now the job keeps running until a delivery stores the model; a later delivery's failed download never clears persisted_model_path or puts the provider's link back over the stored copy. If 15 minutes pass after the task's last update and no model was ever stored, the next read of the job fails it with "The model could not be saved. Your {n} credits have been refunded." (reason Refund for unsaved model {id}, claimed through refunded like every other refund, with the Credits refunded bell). Tripo failures are refunded during the status sync. The run's own failure line in /app is rewritten by cause the same way as a create failure, and the Discord DM now carries the same sentence.

Meshy delivers webhooks at least once, so each side effect is claimed atomically with the claim_meshy_task_flag function: refunded, discord_success_dm_sent, discord_success_logged, discord_failure_dm_sent, discord_failure_logged. Only the caller that flips the flag runs the action, so there is exactly one DM, one log and one refund per task. If a refund fails after being claimed, release_meshy_task_flag clears the refunded flag so a retry can pay it. Notification flags are never released.

Since 2026-10-05 a refunded generation also puts Credits refunded in the workspace bell ("75 credits back"), once per task, so the refund is on record even when the Discord DM is blocked.

Multiview input

Image to 3D accepts extra angles under "Extra views (optional)", up to 4 images in total. Every extra view must be PNG, JPG or WebP ("Please use PNG, JPG, or WebP images."). For the Meshy models the hint is "Add up to 3 more angles (back, sides) for better geometry." and the request goes to Meshy's multi-image endpoint. For Imagine Pro the hint is "Add views in order: left, back, right. Uses 2 or more images."; Tripo requires the front view plus at least one more and reads them in the order front, left, back, right. The route (/api/v1/imagine/3d/mesh) reads image_urls by position and accepts null for an empty slot after the front (the workspace's slot input sends a back view alone as [front, null, back]), keeps the empty places for Tripo and drops them for Meshy's multi-image endpoint; the legacy intake sends a packed list, which still works. A "Remove background" button on the main image runs the background remover before generation ("Background removed.").

Imagine Pro with extra views never worked between 2026-09-02 and 2026-10-12: every run failed at the start with "Tripo API error when creating a task. Error: files or inputs are required for multiview_to_model." and was refunded at once, so no Tripo multiview task exists before the fix. The site sent V3 inputs as one object ({front, back}); V3 wants an array of one-key objects ([{front: url}, {back: url}], an empty view left out). Single-image Imagine Pro and Meshy multi-view were not affected. Someone who asks: the credits came back, and the same views work now.

The workspace's form sends image_ref / image_refs (storage refs from /api/v1/imagine/input-upload, [front, left, back, right] with null for an empty slot); the route checks each is the caller's own upload under tmp/imagine/<user>/ and moves it storage-side into the site bucket under meshy_previews/<user>/ ("Your image upload could not be found. Add the image again and retry." if the move fails), so the providers get a public link. An image sent inline (a data URL, what the legacy pages send) is stored as a file in the site bucket first, under meshy_previews/<user>/, and the link goes to the provider, the log and the task record. That write is made with the service role: the bucket has no storage policy for a signed-in user, so the old user-scoped write was refused ("new row violates row-level security policy") and the base64 travelled on instead, which then broke the Discord log on its 4000 character limit (fixed 2026-09-14; log text is now clamped, and a data URL is never printed in a log).

Using the generated model

Image to 3D shows "Model generated" and "Loading it into the editor is free. You already paid for generation." when done. "Continue" opens the editor with the model; the reset button starts over. Generated files are copied to Zoov storage (model, preview and textures), so they keep working after the provider's links expire, and appear under "Recent generations" on later visits. In the editor the model can be scaled, retextured, reduced to fewer triangles ("Reduce the mesh", free; the paid AI remesh is gone) and exported; export pricing is in the Prop Converter doc.

Common questions

Which model should I pick? Imagine is the default and works for most props. Imagine FAST is quickest, Imagine Detail sharper on complex subjects. Imagine Pro (Pro and Max) gives the highest detail with PBR textures.

What does a generation cost? 130 credits for the Meshy models (75 with Referrer, Standard, Pro or Max) and 55 credits for Imagine Pro. Making the model lighter later with "Reduce the mesh" is free.

Should I lower the polycount to get a cleaner model? No. Below 10,000 polygons surfaces can rip or tear and thin parts, logos and text are the first to go. Keep the default and use "Reduce the mesh" in the workspace if you need a lighter file.

My generation failed. Where are my credits? They are refunded automatically; the failure message and the Discord DM state the amount. If the message says the refund could not be made, staff have been alerted and will restore them.

How long does it take and how will I know? Usually a few minutes. Keep the page open to watch progress, or wait for the Discord DM "Model Ready!".

Why does it say Imagine Pro (or another model) is unavailable right now? Our own account at the provider behind that model is low on credit or unreachable. It is nothing to do with your plan or your credits, and nothing is charged. Imagine, Imagine Detail and Imagine FAST share one provider; Imagine Pro has its own, so another model usually still works. In the workspace the picker marks the model "Unavailable" before you start, and staff are alerted the moment it happens.

Why was my image rejected? The provider refused it on content grounds, and the message says so: "That image was rejected by the 3D generator for content reasons. Try a different picture." Your credits are refunded. Pick another picture; there is no appeal, the rules are the AI provider's own. A different message, "The 3D generator could not read a model from that image", means the picture itself was the problem: use a single subject, in focus, on a plain background.

I get "413" or "the images are too large to send". Before 2026-09-22 the workspace sent pictures inside the request, and anything over about 3MB hit the hosting platform's 4.5MB request cap (a 413, shown as "Could not start the generation (HTTP 413)"). Nothing was charged. Now pictures are resized and uploaded first; a reload picks up the fix. On the legacy pages, use a picture under 3MB.

Can I upload several photos of the same object? Yes, up to 4 views. Meshy uses any angles; Imagine Pro needs at least two and reads them as front, left, back, right.

Topics image to 3dimaginemeshytripomeshpolycountrefunddiscord

Something missing? Ask on Discord