Myrlin Docs
Generate 3D assets for Hytale and Minecraft from text or images. Use the web app or integrate with our API.
What is Myrlin?
Myrlin turns text descriptions and reference images into game-ready 3D assets. It outputs native .blockymodel files for Hytale and .glb files for Minecraft, Blockbench, and other engines.
You can use Myrlin through the web app at forge.myrlin.io, or programmatically through the REST API.
Using the Fabricator
Text to 3D
Describe what you want to create. Myrlin generates a concept image first, then converts it into a textured 3D model. You can iterate on the concept before committing to 3D generation.
- Enter a prompt describing your asset
- Review the concept image. Iterate if needed.
- Approve and generate the 3D model
- Choose your voxel resolution and convert to BlockyModel
- Download in your preferred format
Image to 3D
Upload a reference image instead of typing a prompt. Myrlin uses it as the basis for 3D generation. Works best with clean, single-object images on a simple background.
Retexturing
Apply new textures to an existing 3D model. Useful for creating color variants or upgrading texture quality on completed assets.
Asset Library
All generated assets are saved to your library automatically. From there you can:
- Preview models in an interactive 3D viewer
- Download as .blockymodel, .glb, .bbmodel, .obj, .stl, or .vox
- Generate resolution variants of the same model
- Rename and organize your creations
Your assets are private by default. Featured assets in the Gallery are selected by admins from community submissions.
Export Formats
Pricing
Myrlin uses a token system. Each generation step costs tokens. New accounts receive free tokens to get started.
| Action | Tokens |
|---|---|
| Generate concept image | 1 |
| Iterate on concept | 1 |
| Generate 3D model | 4 |
| Retexture | 3 |
| API generation (full pipeline) | 7 |
Tokens can be purchased individually or through a subscription plan for volume discounts and monthly allocations.
API Reference
The Myrlin API lets you generate 3D assets programmatically. Authenticate with an API key, start a generation, and poll for results.
Authentication
Pass your API key in the X-API-Key header. Keys start with myrl_ and are created in Settings. API access requires an active subscription.
X-API-Key: myrl_your_key_hereBase URL
https://api.myrlin.io/v1Endpoints
| Method | Path | Description | Cost |
|---|---|---|---|
| POST | /v1/generate | Start a generation | 7 tokens |
| GET | /v1/generations/{id} | Poll status and download URLs | Free |
| GET | /v1/balance | Check token balance | Free |
| GET | /v1/usage | Usage history | Free |
| GET | /v1/usage/stats | Aggregated usage stats | Free |
POST /v1/generate
Start a new generation. Returns immediately with a generation ID. Poll GET /v1/generations/{id} for progress and results.
| Field | Type | Required | Description |
|---|---|---|---|
| prompt | string | Yes | What to generate (3-500 chars) |
| workflow | string | No | text_to_3d (default) or image_to_3d |
| style | string | No | hytale (default) or minecraft |
| reference_image_base64 | string | For image_to_3d | Base64 encoded reference image |
| voxel_size | number | No | 0.1 (32px), 0.067 (48px), 0.05 (64px) |
| api_version | int | No | Set to 2 to enable v2 features (target_size). Default: 1 |
| target_size | object | No (v2) | Target dimensions in Hytale units. {"x": 65} for uniform scale, {"x": 65, "y": 12, "z": 3} for per-axis |
GET /v1/generations/{id} Response
When status is complete, download URLs are populated. Poll every 10 seconds.
{
"id": "abf628cc-20e1-46ea-989d-e021f31769ce",
"status": "complete",
"progress": 100,
"prompt": "A steampunk pistol with brass gears",
"glb_url": "https://...",
"blockymodel_url": "https://...",
"texture_url": "https://...",
"thumbnail_url": "https://...",
"tokens_charged": 7
}Status values: pending, generating, complete, failed. Download URLs expire after 24 hours. Re-poll to get fresh URLs.
Examples
Python
import requests, time
API_KEY = "myrl_your_key_here"
BASE = "https://api.myrlin.io/v1"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
# Start generation
gen = requests.post(f"{BASE}/generate", headers=HEADERS, json={
"prompt": "A crystal sword with ice enchantment",
"style": "hytale"
}).json()
# Poll until complete
while gen["status"] != "complete":
time.sleep(10)
gen = requests.get(f"{BASE}/generations/{gen['id']}", headers=HEADERS).json()
print(f"{gen['progress']}%")
# Download
for key in ["glb_url", "blockymodel_url", "texture_url"]:
if gen.get(key):
data = requests.get(gen[key]).content
with open(f"model.{key.split('_')[0]}", "wb") as f:
f.write(data)JavaScript
const API_KEY = "myrl_your_key_here";
const BASE = "https://api.myrlin.io/v1";
const headers = { "X-API-Key": API_KEY, "Content-Type": "application/json" };
// Start generation
const gen = await fetch(`${BASE}/generate`, {
method: "POST", headers,
body: JSON.stringify({ prompt: "A crystal sword", style: "hytale" }),
}).then(r => r.json());
// Poll until complete
let status = gen;
while (status.status !== "complete") {
await new Promise(r => setTimeout(r, 10000));
status = await fetch(`${BASE}/generations/${gen.id}`, { headers })
.then(r => r.json());
}
console.log("GLB:", status.glb_url);
console.log("BlockyModel:", status.blockymodel_url);cURL
# Start generation
curl -X POST "https://api.myrlin.io/v1/generate" \
-H "X-API-Key: myrl_your_key_here" \
-H "Content-Type: application/json" \
-d '{"prompt": "A crystal sword", "style": "hytale"}'
# Poll status
curl "https://api.myrlin.io/v1/generations/GENERATION_ID" \
-H "X-API-Key: myrl_your_key_here"
# Check balance
curl "https://api.myrlin.io/v1/balance" \
-H "X-API-Key: myrl_your_key_here"V2: Target Dimensions (Python)
Set api_version to 2 and provide target_size to control output model dimensions. The model is rescaled after voxelization to match your target.
import requests, time
API_KEY = "myrl_your_key_here"
BASE = "https://api.myrlin.io/v1"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
# Generate a sword scaled to 65 units along X axis
gen = requests.post(f"{BASE}/generate", headers=HEADERS, json={
"prompt": "A Persian scimitar sword for Hytale",
"style": "hytale",
"api_version": 2,
"target_size": {"x": 65} # Uniform scale: longest axis = 65 units
}).json()
# Or specify all three axes independently
gen = requests.post(f"{BASE}/generate", headers=HEADERS, json={
"prompt": "A tower shield for Hytale",
"style": "hytale",
"api_version": 2,
"target_size": {"x": 8, "y": 40, "z": 30} # Per-axis dimensions
}).json()
# Poll and download as usual
while gen["status"] != "complete":
time.sleep(10)
gen = requests.get(f"{BASE}/generations/{gen['id']}", headers=HEADERS).json()Error Handling
| Status | Meaning |
|---|---|
| 401 | Invalid or missing API key |
| 402 | Insufficient token balance |
| 403 | Access denied (wrong owner or no subscription) |
| 404 | Generation not found |
| 429 | Rate limit exceeded (check Retry-After header) |
If generation fails after tokens are charged, they are automatically refunded.
