Appearance
Moonomat FAQ
Welcome to the Moonomat FAQ. New here? Start with Getting Started. If you can't find your answer, write to support@moonomat.com.
Getting Started & Orientation
- What is Moonomat, and who is it for?
- Do I need to install anything?
- Do I need an account?
- What's free, and what does Full Moon add?
- What is Moon, and do I have to learn it?
- What is a bundle?
- Where do my files live?
- Is my work private?
- How do I import existing files into a bundle?
What is Moonomat, and who is it for?
Moonomat is parametric 3D for the web. You write 3D models as text files, and every parameter stays live: in the editor while you work, and on your web page once you publish. It is built for people who put 3D on websites (product and marketing pages, configurators, technical explainers), and for anyone who wants precise, reproducible 3D without a desktop suite.
What makes it different:
- Designs as text. Every design is a small Moon file. Text is exactly reproducible, easy to diff and remix, and good for iterative work: change a parameter and the whole model rebuilds.
- Real modeling operations. Booleans, fillets, chamfers, extrude, revolve, sweep, loft, 2D-to-3D workflows, PBR materials, and data-driven generation.
- Built on glTF, the standard for 3D on the web. Moonomat produces display-ready
.glbfiles with automatic UV unwrapping, texture and ambient-occlusion baking, ready for any web viewer, game engine, or AR scene. The free Moonomat Viewer puts the live parametric design on your own site. - The Design agent. Describe what you want, or start from photos, a spec sheet, or CAD, and the agent writes, runs, and refines the design for you.
- Data visualization. Import
.csv,.json, GeoJSON, or NumPy data and turn rows, tensors, and graphs into geometry.
It runs in any modern browser and can be installed as an app.
Do I need to install anything?
No. Moonomat runs entirely in the browser, with no download and no plugins. You can install it as an app to get a window of its own. In Chrome or Edge on the desktop, use the install icon in the address bar. On Android, open the Chrome menu (⋮) and choose Add to Home screen.
Do I need an account?
Not to design. The editor, the Moon runtime, the 3D viewer, unlimited bundles, and sharing by link all work without signing in. Your work is stored on your device.
A free account (Google sign-in) adds the things that need an identity: the Design agent, GitHub sync, and GitHub-hosted publishing. When you sign in for the first time, the bundles you created on that device are moved into your account. Nothing is lost.
What's free, and what does Full Moon add?
Free, without an account: the full editor, the Moon runtime, the 3D viewer, parameters, unlimited local bundles, the asset libraries, and sharing by link. Free, with an account: the Design agent with a daily allowance of requests (bring your own AI), plus GitHub sync and publishing.
Full Moon, the paid plan, lifts the limits: unlimited Design agent use, still with your own AI (your key or your own machine; we never resell or mark up inference), plus Google Drive sync. It is also the best way to support Moonomat's development.
What is Moon, and do I have to learn it?
Moon is a small declarative language for parametric 3D design. A Moon document is a YAML hierarchy of nodes: primitives like Box or Sphere, operations like Union or Extrude, and reusable compositions you define yourself. The Moon runtime evaluates the node graph and produces one asset, usually a .glb, but also .svg, .png, or .json.
You don't have to learn it. The Design agent writes and maintains Moon for you, and it is free to try with an account. Learning the basics is still worthwhile, so you can review what the agent produced, tweak details by hand, or work without AI. The specification and API reference cover everything.
What is a bundle?
A bundle is the unit of work in Moonomat: a self-contained folder of Moon files and assets (models, images, data). Its entry file is main.moon, the design the bundle shows when run or shared. A bundle without a main.moon is a library bundle, a collection of reusable parts.
Bundles are portable. A whole bundle packs into a single .moons text file (or .moon.zip in binary form) for sharing, publishing, and syncing. You can keep as many bundles as you like, on every tier.
Where do my files live?
By default on your device, in the browser's storage (IndexedDB). Your content is never uploaded to Moonomat's servers.
Browser storage quotas typically allow from hundreds of megabytes up to many gigabytes per site. Chrome allows up to 60% of total disk space. Large binary imports count against this.
For cloud backup and working across devices, connect the Transfer panel to your own GitHub repositories (free, signed in) or Google Drive (Full Moon). Both live in your accounts. Moonomat never holds your data.
Is my work private?
By default, yes. Your bundles live only on your device, like files in desktop software. Keep your device secure and they are yours alone.
Sharing is always an explicit act: publishing a link, pushing to a GitHub repo, syncing to Drive, or downloading and sending a file. Each of those paths is described below.
How do I import existing files into a bundle?
Two ways:
- From the Explorer. Drag and drop files or folders onto the Explorer panel, or right-click and choose Upload….
- By URL. Reference a publicly hosted file from an
import:node in Moon. Browsers may block the request if the host doesn't allow cross-origin access (see the CORS question below).
Supported formats include glTF (.glb/.gltf), .obj, .stl, STEP CAD files, .svg, images, fonts, and data (.csv, .json, GeoJSON, NumPy). The full table is in the Moon section below. Other files, such as a PDF spec sheet or a DXF drawing, can live in the bundle too: the app displays them and the Design agent reads them as references, but Moon itself cannot import: them.
The Design agent
- What can the Design agent actually do?
- What do I need before I can use it?
- Which AI backends are supported?
- Which model should I pick, and what is "thinking effort"?
- How does the free request meter work?
- Does Moonomat charge for AI usage?
- Can the agent see my 3D scene?
- Can the agent work from photos, drawings, or CAD?
- How do sessions work?
- How do I write a good prompt?
- Can I stop or undo something the agent did?
- Is my prompt content sent to third parties?
- Can the agent work offline or fully on-device?
What can the Design agent actually do?
The agent reads and searches the files in your bundle, writes and edits Moon, and checks its own work. Every .moon file it writes or edits is validated and run immediately. If validation fails, the agent sees the errors and fixes them. After a successful change it renders the 3D result and compares the image against what you asked for before reporting back.
Each agent step lands on the same undo stack as your own edits, so one Undo reverts it (see below).
What do I need before I can use it?
Two things:
- Sign in free (Google). The agent is metered per account, and its connection settings live in your profile.
- Connect an AI backend. Moonomat does not bundle or resell AI. You bring your own: an OpenRouter key, your Claude or ChatGPT subscription, or a server you run yourself.
The Design panel walks you through both. Signed out, it offers the sign-in and lists the bring-your-own-AI routes. Signed in without a backend, it shows Connect your AI with the three routes. Then start prompting. Free accounts get a daily allowance of requests; Full Moon removes the cap.
Which AI backends are supported?
Three kinds, switchable in the Design panel header:
- OpenRouter. One account for most current AI models. Click Connect, authorize Moonomat on OpenRouter (you can cap its spending there), and pick a model. You pay OpenRouter directly; Moonomat takes no cut.
- Claude / Codex. Connect the agent to your Claude (Pro/Max) or ChatGPT (Plus/Pro) subscription through a small open-source bridge you run on your computer (setup instructions are in the panel). A good fit if you already pay for one of them.
- Your own endpoint. Any OpenAI-compatible server, local or remote: Ollama, LM Studio, llama.cpp, or vLLM on your own machine, or a cloud API (OpenAI, Groq, Together, Mistral, xAI) with your key. A local Ollama endpoint is pre-configured. The model needs a large context window, tool use, and vision, and a local one needs a machine that can run it.
All backends are treated the same: sign-in plus the same meter, because in every case the inference is yours.
Which model should I pick, and what is "thinking effort"?
On OpenRouter we mark recommended models that we have tested on Moon tasks. You can pick anything that meets the minimum requirements (large context, image understanding). There is a real trade-off between capability and cost. Cheap models handle small edits; for complex iterative design, state-of-the-art models usually save more time than they cost.
Thinking effort (Off to Max) controls how much internal reasoning the model spends before acting. Start low and raise it when the agent struggles with a multi-step task.
How does the free request meter work?
One "send" is one request, no matter how many files the agent reads, edits, or runs within it. The free allowance is currently 10 requests per 24 hours, in a fixed window that starts with your first request. When you hit the cap, the app tells you when it resets. Requests that fail are refunded. The allowance may change as we learn how the free tier is used.
The meter exists because what Full Moon sells is unlimited use of the Design agent. The AI underneath is always brought by you, at cost.
Does Moonomat charge for AI usage?
No. Moonomat does not earn money on your AI spend. OpenRouter bills you directly for tokens. Claude / Codex usage runs on your existing Claude or ChatGPT subscription. Your own endpoint is your own hardware or API contract. Moonomat charges only for the plan (unlimited agent use and Drive sync), never for inference. There is no markup, no reselling, and no fallback to some other provider.
Can the agent see my 3D scene?
Yes, in two ways:
- It looks by itself. After edits, the agent renders the result (a 3×3 multi-view with nine standard angles) and compares it against your request.
- You show it something specific. The attachment menu (paperclip) offers Capture Current 3D View (exactly what you framed), Capture Scene Multi View, Upload Images (references, sketches; on a phone also Take Photo), and Files from Bundle (images and text files). You can also drop images and text files onto the message box. PDFs, drawings, and CAD go into the bundle instead. The agent reads them there with its own tools when you point to them.
Can the agent work from photos, drawings, or CAD?
Yes. Put the material into the bundle (or attach images) and ask. The agent reads photos, PDF spec sheets, DXF drawings, and STEP files as references. A STEP file serves as a dimensional reference rather than a shape to trace.
For a full reconstruction there is an experimental Reconstruct mode in the attachment menu. It runs an autonomous build, review, and revise loop against your references for a time budget you choose (30 minutes to 5 hours), and it needs a model that understands images. It is the agent's most token-hungry way of working, so pick the model and budget accordingly.
How do sessions work?
The agent keeps one session per bundle, per backend. Switch bundles and you are back in that bundle's conversation. Switch backends and each keeps its own thread. New (in the panel header) starts fresh, and the old session for that bundle is deleted.
Sessions keep the context focused. Start a new one when you switch to an unrelated task.
How do I write a good prompt?
Be specific, and work in steps.
A bad prompt:
"Generate a 3-bedroom house."
The model can't know what your house should look like, and unlike an image generator it won't invent an "average" one. You stay the designer; the agent turns your intent into Moon.
A good workflow:
"Make a box, 1 meter, blue." "Now put a red sphere on top, 20 cm, centered." "Make the sphere smaller and matte."
Describe the object the way you would build it. If something is wrong, say so plainly: "that should sit on the floor, not float". If a file has an error, ask the agent to fix it.
Can I stop or undo something the agent did?
Yes, on three levels:
- Undo (toolbar button, or
undoin the terminal). Each agent step is a single entry on the same undo stack as your own edits, so one undo reverts a whole step. Thehistoryterminal command lists what would be undone. - Ask the agent. "Revert the last change", "remove the box you added". Natural language works.
- Duplicate first. For big experiments, duplicate the bundle beforehand (bundle menu → Duplicate), or push to GitHub so you can compare and roll back via Transfer.
Is my prompt content sent to third parties?
It is sent to the backend you chose, and only there. With OpenRouter, prompts, attached images, and tool calls go through OpenRouter to the model provider you selected (check their retention policies before sending anything sensitive). With Claude / Codex, they go to Anthropic or OpenAI under your subscription. With a local endpoint such as Ollama, everything stays on your machine.
Can the agent work offline or fully on-device?
The agent can run on your own hardware. Connect a local endpoint like Ollama or LM Studio and no prompt leaves your machine. Keep in mind that agentic coding is demanding: small local models are fine for parameter tweaks and simple edits, while complex construction works better with frontier models. Sign-in and the meter still apply; they are what the free tier and Full Moon differ on.
The App
- What does each sidebar panel do?
- How do I create, rename, duplicate, or import a bundle?
- Which file types can the app display?
- What does the Run button do?
- Why is the Run button sometimes greyed out?
- Does the Parameters panel change my file?
- What is the terminal?
- Are there keyboard shortcuts?
- How do I navigate the 3D view?
- Can I have multiple browser tabs open?
- What is the
export/folder? - How large can a bundle be?
What does each sidebar panel do?
| Panel | What it does |
|---|---|
| Design | The Design agent: prompt input, conversation, backend and model pickers, thinking effort, and the attachment menu. Signed-out users see a sign-in invitation here, because the agent needs a (free) account. |
| Explorer | The bundle's file tree. Selecting a file shows it in the matching viewer (3D model, image, SVG, data, …) and, for text formats, in the Editor. Right-click for rename, delete, upload, and download; new file and new folder are buttons in the panel header. Folders also appear when you write a path like parts/lid.moon, or with mkdir in the terminal. |
| Search | Full-text search over every file in the bundle, with results grouped by file. |
| Editor | A full code editor (the engine that powers VS Code). Moon files get YAML syntax highlighting, schema-aware validation and hovers, and error markers from the last run. Saving is automatic. |
| Parameters | Interactive controls for the selected .moon file's parameters: sliders, color pickers, toggles, and more, derived from the parameters' metadata. See the Parameters question below. |
| Transfer | Manual push, pull, and diff between local bundles and your GitHub repositories (free, signed in) or Google Drive (Full Moon). See the Sync section. |
| Publish | Share links and QR codes, the GitHub-hosted Viewer link, the website embed snippet, and downloads. See the Sharing section. |
| Libraries | Browse example designs and assets, preview them, and copy anything into your bundle as a starting point. Available without an account. |
| Settings | The dark / light theme, and the 3D viewer controls: rendering engine, brightness, background, floor grid units and scale, coordinate-system overlay, wireframe, vertex normals. Also live scene statistics (vertices, triangles, materials, bounds). |
| Help | Links to this documentation and the legal pages, app and Moon runtime version info, live memory usage, and a Restart runtime button. |
There is also a terminal. The status strip in the header shows the most recent command; click it to open the full terminal (see below).
How do I create, rename, duplicate, or import a bundle?
The bundle name in the header is a menu: New Moon bundle, Import Moon bundle… (open a .moons or .moon.zip from disk), Duplicate, Rename, and Delete, plus the list of your bundles for switching. Bundles are unlimited on every tier. A fresh bundle starts with a small parameterized main.moon that runs as-is.
Which file types can the app display?
Each format opens in a dedicated viewer: 3D models (.glb/.gltf, plus imported .obj/.stl/STEP shown as models), images (including WebP, AVIF, and TIFF), SVG, PDF, DXF drawings, JSON, CSV/TSV tables, NumPy arrays, fonts, Markdown, HTML, and plain text for everything else. Selecting a .moon file shows its latest run result.
What does the Run button do?
Run (or Ctrl+Enter) evaluates the selected .moon through the Moon runtime: parse, link (resolve imports and references, type-check), evaluate the node graph, bake materials for glTF output, and write the result to the bundle's export/ folder. Then it shows the result in the viewer. Errors come back with line numbers and appear as markers in the Editor.
You don't need to run constantly. A bundle that has never produced output runs automatically when opened, and the viewer always shows the newest result for the selected file.
Why is the Run button sometimes greyed out?
Run needs a runnable .moon file selected and an idle runtime. It is disabled while another run is in flight, including in another tab, because the Moon runtime is shared across tabs. Long runs can be cancelled with Ctrl+C in the terminal.
Does the Parameters panel change my file?
If a .moon file declares params:, the Parameters panel (and the standalone Viewer) render interactive controls for them: sliders with bounds, number and text inputs, color pickers, toggles, vector fields. The controls come from the parameter metadata (kind:, min:/max:/step:, choices:, label:, doc:, category:; see the Moon section).
Tweaking parameters never edits your source file. The values run through a generated wrapper under export/, and the viewer shows that result. Reset discards the overrides and returns to your defaults.
What is the terminal?
A command line over the open bundle: POSIX-style file verbs (ls, cat, mv, …) and moon … commands for everything else (moon run, moon render, moon push, moon pull, moon diff, …). The terminal, the UI, and the Design agent are three front-ends to the same commands. Everything lands on the same undo stack and the same history, and the terminal doubles as the app's activity log. help lists everything; see the Terminal reference.
Are there keyboard shortcuts?
- Ctrl+Enter runs the open
.moonfile. - Ctrl+P opens a file by name.
- Undo / Redo are toolbar buttons (agent steps count as single entries). Inside the Editor, Ctrl+Z is the editor's own text undo.
- The Editor supports the standard VS Code editing shortcuts.
- The 3D view has its own keys; see "How do I navigate the 3D view?" below.
- Saving is automatic. There is nothing to press.
How do I navigate the 3D view?
The "?" button below the view cube shows the full controls legend in the app. In short:
- Left-drag rotates around the focus point, the point the camera aims at. It follows what you look at automatically, and a double-click (or double-tap) sets it to the exact spot you clicked.
- Scroll moves the camera toward your cursor. Middle-drag (or Shift + left-drag, or a two-finger drag) pans.
- Hold the right mouse button to fly. The mouse looks around, and W A S D (or the arrow keys) move, faster the longer you hold them; Q / E go down and up, Shift speeds up. Flying speed adapts to your surroundings, slowing down near detail and speeding up in the open. Keys work after clicking the 3D view.
- F frames the model again. Clicking a side of the view cube snaps to that view and resets the focus point.
Can I have multiple browser tabs open?
Yes. Write access follows the tab you are using: the active tab holds the bundle's write lock, and a background tab takes over automatically the moment you return to it. The Moon runtime is shared across tabs, so only one run executes at a time.
What is the export/ folder?
Generated output. Every run writes its result there, mirroring the source path (parts/lid.moon → export/parts/lid.glb), along with screenshots from moon render. It persists locally so results survive reloads, but it is not included when a bundle is packed, published, or synced, since it can always be regenerated. Right-click any file in it to download; remove files with rm in the terminal.
How large can a bundle be?
Bundle imports are capped at 2 GiB, and total local storage is governed by your browser's quota (Chrome allows up to 60% of total disk). In practice, keep an eye on large binary imports. Heavy .glb files and high-resolution textures take most of the space.
Sharing & Publishing
- How do I share a design?
- What is Moonomat Viewer?
- Can people play with my design's parameters?
- How do I embed a design on my own website?
- Can my page set the parameters from JavaScript?
How do I share a design?
Open the Publish panel. You share the design itself, so people can still change its parameters:
- Inline link & QR code. The whole design travels inside the URL (up to roughly 8,000 characters after compression; small and medium designs fit easily). Anyone with the link sees it right away, without an account.
- GitHub link, for bigger bundles. Push the bundle to a public GitHub repository in Transfer, and Publish gives you a permanent hosted Viewer link.
- Download. The packed
.moons/.moon.zip, or the rendered.glbfromexport/, for use anywhere.
What is Moonomat Viewer?
A free, viewer-only companion app at moonomat.com/view. No account, no editor: just the model, orbit controls, and its parameter panel. Every shared link and embed opens there. A library bundle (one without main.moon) opens as a thumbnail gallery of its designs. The small Moonomat mark in the corner leads back to Moonomat, and Remix in Moonomat opens the design as a new bundle in the editor.
Can people play with my design's parameters?
Yes. The Viewer shows interactive controls for every parameter your design declares (see parameter metadata in the Moon section). You can also pre-set values in a shared URL, for example ?params.radius=0.4¶ms.style=matte, to share specific variants of one design, or hide the panel with ?parameters=false.
How do I embed a design on my own website?
Two lines of HTML and a file you host. Download the packed bundle from the Publish panel (.moons, or .moon.zip when it holds binary assets), put it beside your other static files, and point the <moonomat-viewer> element at it:
html
<script src="https://moonomat.com/embed/v1.js"></script>
<moonomat-viewer src="/models/lounge-chair.moons"></moonomat-viewer>Your page gets the complete Viewer, with 3D orbit and parameters, and AR on phones that support it. The element is an iframe onto the Viewer, so styles and scripts never leak either way. The model is rebuilt in your visitor's browser: there is no render server, nothing is metered by traffic, and no account is needed on either side.
If you would rather not host the file, Publish → Embed generates the same snippet loading the bundle from your public GitHub repository. The bundle must be pushed, in sync, and public there; the hosting is your repo, independent of your Moonomat plan. And if you have no page of your own, share a moonomat.com/view link instead.
Can my page set the parameters from JavaScript?
Not yet. The element's attributes (src, theme, poster, …) are reactive, so your page can swap the design or the theme, but there is no API to read or set parameters. Visitors set them in the Viewer's own controls, or you pre-set them in a /view link with ?params.… as described above.
Sync & Collaboration
- How does the Transfer panel work?
- How do I connect GitHub?
- How do I connect Google Drive?
- How do I work across multiple devices?
- Can several people collaborate on one bundle?
- What if the same file changed on two devices?
- How are my GitHub, Google, and AI tokens stored?
How does the Transfer panel work?
Transfer is manual sync of whole bundles between your device and remotes you own. Push uploads your local state, Pull fetches the remote state, and Diff shows what differs (modified, added, deleted, with per-file diffs) before you decide. Bundles travel as packed .moons files. Nothing syncs on its own.
How do I connect GitHub?
Free, with a sign-in. Install the Moonomat GitHub App on your account, then grant it access to specific repositories via Manage repository access. Installation alone grants nothing. Public and private repos both work, and permissions are governed entirely by GitHub. For security, signing out of Moonomat clears the GitHub connection; click Connect again next time.
GitHub also suits .moon work well in general: text designs can be diffed, versioned, and reviewed like code.
How do I connect Google Drive?
Drive sync is part of Full Moon. Connecting asks for a narrow Google Drive permission that only covers files Moonomat itself creates, inside a Moonomat folder in your Drive. Moonomat cannot see anything else in there. Your synced bundles remain plain files in your own Drive, so even if you downgrade, everything stays reachable at drive.google.com.
How do I work across multiple devices?
Connect the same remote on each device and follow the usual rhythm: pull before you start, push when you are done. Any device that has pulled the bundle holds a full local copy. Transfer is sync, not streaming.
Can several people collaborate on one bundle?
Yes, through a shared remote. Add collaborators to the GitHub repo, or share the Drive folder with their Google accounts. Everyone works on their own local copy and syncs with Push and Pull. Moonomat does not merge concurrent edits (see the next question), so agree on who edits what, or work in turns.
What if the same file changed on two devices?
Diff shows the conflict, but you resolve it by hand. Moonomat never auto-merges. A safe routine: duplicate your local bundle (or copy the affected file), Pull, merge your changes back in the Editor, then Push. Since designs are text, the merge itself is straightforward.
How are my GitHub, Google, and AI tokens stored?
Standard OAuth practice: the tokens are held in your browser under your signed-in profile, never on Moonomat's servers. Google sign-in also uses a secure cookie so you stay signed in between visits; it expires on its own or when you revoke access. Signing out clears the tokens from browser storage and keeps your bundles on the device. You can also revoke Moonomat from the provider's side at any time:
- Google Account permissions
- GitHub → Settings → Applications → Installed GitHub Apps
- OpenRouter key management
The Moon Language
The full picture lives in the specification, the API reference, and the ECMAScript guide. These are the questions that come up most.
- What is a minimal
.moonfile? - Why does my file need
moon: "1.0"at the top? - What is
doc:for? - What are Compositions, Operations, and Expressions?
- When should I use
op:vs.expression:? - When should I use
pipe:vs. nestedinput:? - When should I use
group:vs.items:vs.Union? - What's the difference between
params:andassets:? - How do I add sliders, colors, and choices to parameters?
- How do I reuse code across multiple
.moonfiles? - What does
produces:do? - How do
if:andmatch:work? - Which JavaScript can I use inside an
expression:? - How does randomness work in Moon?
- Can expressions fetch URLs or read files?
- What coordinate system and units does Moon use?
- What file types can I import, and what gets lost?
- Why did a boolean strip my texture mapping?
- What is "CSG", and why does watertightness matter?
- How do I apply a material to only part of my model?
- What's the difference between
ApplyMaterialandOverrideMaterial? - How do I use an image as a texture?
- How do I make glass, car paint, velvet or glowing parts?
- How do I make rounded edges?
- How do I turn 2D SVG contours into a 3D object?
- How do I create 3D text?
- How does the 2D SVG plane map into 3D space?
What is a minimal .moon file?
The root of a Moon document is always a Composition. It declares the language version, a doc: description, and a render: node, the thing it produces.
yaml
moon: "1.0"
doc: |
A sphere with a radius of 0.5 meters
render:
op: Sphere
with:
radius: 0.5Why does my file need moon: "1.0" at the top?
It declares the language version. The current version is 1.0, but the specification is still being refined. The app and runtime always ship the latest spec, and files may need small updates as things evolve. Only the root composition carries this property; inner compositions must omit it.
What is doc: for?
On a composition it is required. It documents the component's contract and is the most valuable documentation you can write (the Design agent reads it too). On any other node it is an optional annotation, a free-form note the runtime ignores. Regular YAML # comments also work anywhere.
What are Compositions, Operations, and Expressions?
Three ways a node produces its output:
- Composition (
render:). A reusable blueprint you define, like a function declaration: optionalparams:(the external interface), optionalassets:(internal named pieces), and arender:as its return value. Invoked withuse:. - Operation (
op:). A call to a built-in function by name (Box,Union,Transform, …). This is the everyday form. - Expression (
expression:). An escape hatch into sandboxed ECMAScript for logic that is awkward as a node graph: loops, math, array transformations.
When should I use op: vs. expression:?
Prefer op:. The declarative form is shorter, type-checked at link time, cached more precisely, and easier for both humans and the Design agent to read. Use expression: only for real logic. Even there you can call operations as functions (Box({...}), Union([...])), so you never have to choose one world exclusively.
When should I use pipe: vs. nested input:?
They produce identical results. pipe: reads top to bottom like a Unix pipe and is the better choice for linear chains of more than two steps. Nested input: fits single steps and branching graphs.
When should I use group: vs. items: vs. Union?
group:merges assets of the same type with the cheapest strategy: for glTF a lazy scene assembly (no geometry math), for tables row concatenation, for JSON a deep merge.items:builds a list to pass to a multi-input operation (Union,Difference,Intersection,Stack, …). It merges nothing itself.Unionis a true boolean solid merge, computing actual geometry.
Rule of thumb: group: to assemble a scene; items: + Union to fuse overlapping solids into one watertight body.
What's the difference between params: and assets:?
params:is the composition's external interface: defaults the caller can override viawith:. Think function arguments.assets:are internal named intermediate results, invisible from outside. Use them to break a complexrender:into labeled pieces or to reference a subgraph several times.
How do I add sliders, colors, and choices to parameters?
Declare a parameter in expanded form, a default: plus metadata, and every parameter UI (the Parameters panel, the Viewer, shared links) picks it up:
yaml
params:
radius:
default: 0.5
kind: number
min: 0.1
max: 2
step: 0.1
label: Radius
doc: Outer radius in meters
category: Shapekind: selects the widget: number, string, boolean, color, position, direction, rotation, scale, or raw. Numbers and strings take choices: for dropdowns; label:, doc:, and category: control presentation and grouping. Plain scalars get a sensible widget automatically; everything else without a kind: is edited as raw Moon. Metadata is mostly advisory UI information, with two exceptions: a default: outside its own min/max or choices is a link error, and direction normalizes while rotation wraps to 0–360 at evaluation.
How do I reuse code across multiple .moon files?
Treat Moon files as parameterized components:
- Split a scene into files with one responsibility each (a door, a chair, a wing profile). Each can be viewed and run on its own.
- Expose configurable properties via
params:with sensible defaults. - Pull a component in with
import:and invoke it withuse::
yaml
render:
use:
import: ./components/chair.moon
with:
height: 0.9Importing the same file many times is cheap, because identical subgraphs are computed once (see caching, below). The Libraries panel is this pattern at scale: browse ready-made components and copy them into your bundle.
What does produces: do?
An expression returns a data asset (numbers, strings, arrays, plain objects) by default. If it produces anything else, such as meshes, polygons, an image, a material, or a graph, declare it (produces: MESHES, POLYGONS, IMAGE, MATERIAL, or GRAPH):
yaml
expression: |
Union(positions.map(p => Transform({ input: Box({ size: [1,1,1] }), translate: p })))
produces: MESHESThis lets the linker type-check the document before running it, and the runtime verifies the declaration against what actually comes back. You get precise errors instead of mysterious failures.
How do if: and match: work?
if: is for binary boolean branches. The condition must be a real boolean (no truthy/falsy coercion), else: is required, and both branches must produce the same asset type. match: is for multi-way selection on a discrete key, such as enum-style params or three or more named branches, with an optional default:. Only the selected branch is evaluated; the others cost nothing.
Which JavaScript can I use inside an expression:?
Modern ECMAScript 2023, in a strict sandbox. Expressions are pure functions of their inputs, with no network, filesystem, clock, or mutable global state. async/await and class are rejected at parse time (scripts are synchronous; use factory functions), and a multi-statement script must end in an explicit return.
The ECMAScript guide is the full reference: every available method, the exact sandbox rules, worked patterns, and every example verified against the Moon runtime.
How does randomness work in Moon?
Deterministically. The runtime caches aggressively, and identical inputs must produce identical output, so Math.random() and Date.now() are blocked inside expressions.
Instead, seed randomness through parameters. Use the built-in RandomNormal({ shape, seed }) for random tensors, or expose a seed param and derive values from it. The result is reproducible, and changing the seed gives you controlled variations.
Can expressions fetch URLs or read files?
No. Expressions are fully sandboxed, for security and reproducibility. External data enters only through import: nodes, which are visible in the document.
Remote imports are subject to CORS: the host must explicitly allow browser access (Access-Control-Allow-Origin). Public CDNs and GitHub raw URLs typically do; arbitrary websites often don't, and no web app can bypass that. The reliable workaround: download the file once, drop it into your bundle, and import it by relative path.
What coordinate system and units does Moon use?
The glTF 2.0 convention: right-handed, Y-up, +Z toward the viewer. All measurements are in meters, so model at real-world scale (a chair is 0.8, not 80). STL and STEP files are rotated from Z-up to Y-up automatically on import. Z-up content arriving as .glb/.gltf/.obj (for example from Blender) typically needs a Transform with rotate: [-90, 0, 0]. Imperial units aren't built in; convert in an expression (inches * 0.0254).
What file types can I import, and what gets lost?
| Extension | Becomes | Notes |
|---|---|---|
.glb, .gltf | 3D model | Full fidelity — materials, UVs, scene graph preserved. Draco-compressed meshes and WebP textures are decoded; meshopt and KTX2 are not supported. |
.obj | 3D model | Mesh only; .mtl materials are ignored. |
.stl | 3D model | Mesh only; STL has no materials. Rotated from Z-up to Y-up. |
.step, .stp | 3D model | CAD B-rep, tessellated on load. Rotated from Z-up to Y-up. |
.svg | 2D contours | Paths tessellated to polygons; curve data not retained. |
.csv | Data (table) | Numeric and text columns auto-detected. |
.npy, .npz | Data (tensor) | NumPy arrays. |
.json, .geojson | Data | Verbatim. |
.png, .jpg/.jpeg, .webp, .bmp, .gif, .tga, .tif/.tiff | Texture | Channel count auto-detected. |
.ttf, .otf | Font | For the Text operation. |
.moon | Composition | A full component with its params. |
If your source tool can export .glb, prefer it over .obj/.stl; it is the richest format. And prefer files inside your bundle over remote URLs: no CORS trouble, no re-fetching.
Why did a boolean strip my texture mapping?
The most common gotcha. A 3D asset exists in one of three internal forms (raw glTF, triangle mesh, and CSG, a watertight solid), and boolean operations promote to CSG and back. Constant-color materials survive the round-trip; UV coordinates do not, so textured materials lose their mapping.
Strategies:
- Do the booleans first, then re-establish the mapping afterwards with
TileMaterialorBakeMaterial, or apply materials after the boolean withApplyMaterial. - Use
group:instead ofUnionwhen you only want to assemble a scene. It preserves everything.
What is "CSG", and why does watertightness matter?
Constructive Solid Geometry represents shapes as closed, watertight solids. That is the only setting where booleans are well-defined, because a shape needs an unambiguous inside and outside. All Moonomat primitives are watertight. Imported meshes may not be, and the runtime refuses a boolean on an open surface with an error naming the offending mesh. If you see that error on an import, the mesh has holes or unwelded seams; fix it in the source tool or export it as a closed solid. A boolean that legitimately produces nothing (say, subtracting a box that contains the whole shape) returns an empty result without complaint.
How do I apply a material to only part of my model?
Apply materials to the parts before combining them. ApplyMaterial affects only its input: subgraph, and later grouping preserves what is already applied:
yaml
render:
group:
- op: ApplyMaterial
with:
material: { asset: red_matte }
input:
op: Box
with: { size: [1, 0.1, 1] }
- op: ApplyMaterial
with:
material: { asset: blue_metal }
input:
op: Sphere
with: { radius: 0.3 }What's the difference between ApplyMaterial and OverrideMaterial?
ApplyMaterialfills in materials only where none exist yet. It is a default; existing materials on inner parts win.OverrideMaterialrepaints everything unconditionally.
How do I use an image as a texture?
Import the image into your bundle and pass it to Material. Image files are recognized as textures automatically:
yaml
op: Material
with:
color:
import: ./textures/wood_Color.jpg
normal:
import: ./textures/wood_NormalGL.jpg
roughness:
import: ./textures/wood_Roughness.jpgPer the glTF conventions: color is sRGB, normal maps use the OpenGL ("GL") variant, and roughness and metallic are single-channel. textureSizeInMeters sets the physical size of one texture tile on the surface (1.0 is one square meter, 0.25 a much finer repeat). That is why seamlessly tileable textures work best, which is the standard format PBR libraries ship.
How do I make glass, car paint, velvet or glowing parts?
Material takes the same extended surface properties as glTF's KHR_materials_* extensions, with the same names and defaults. Anything you would set in Blender's Principled BSDF or a three.js MeshPhysicalMaterial maps directly:
yaml
crystal:
op: Material
with:
color: [1, 1, 1, 1]
roughness: 0.03
transmission: 1 # refractive glass — keeps reflections, dial stays saturated
ior: 1.77 # sapphire; window glass 1.5, water 1.33, diamond 2.42
thickness: 0.0014 # meters, for the volume tint below
attenuationColor: [0.85, 0.93, 1]
attenuationDistance: 0.02
paint:
op: Material
with: { color: [0.6, 0.02, 0.02, 1], metallic: 0.5, roughness: 0.4, clearcoat: 1, clearcoatRoughness: 0.05 }
velvet:
op: Material
with: { color: [0.2, 0.02, 0.05, 1], roughness: 0.9, sheenColor: [0.6, 0.3, 0.4], sheenRoughness: 0.4 }
neon:
op: Material
with: { color: [0, 0, 0, 1], emissive: [1, 0.2, 0.05], emissiveStrength: 8 }transmission is physically based see-through. Unlike a color alpha below 1, which just fades the surface, it refracts what is behind it and keeps the surface's own highlights. One caveat: a viewer that lacks KHR_materials_transmission renders such a surface opaque. Moonomat's viewer, model-viewer, three.js and Blender support it. For a lowest-common-denominator transparency (say, a download meant for older tools) use color alpha instead. specular / specularColor and unlit are available too. In the texture variant of Material, properties that have a texture slot in glTF carry a …Factor name instead.
How do I make rounded edges?
Four tools for four situations:
Chamfermakes a flat cut across an edge (distance). Cheap, and it looks engineered.Filletis true circular rounding (radius). Organic, but the radius must stay below half the thinnest feature or that feature collapses. Heavier to compute.RoundedBoxis the way to get a rounded box. Use it instead ofBoxplusFillet; it is much faster and exact.Smoothis subdivision smoothing that preserves sharp edges above an angle threshold. Good for organic shapes.
How do I turn 2D SVG contours into a 3D object?
Four operations, four kinds of geometry: Extrude (straight up, with optional twist and top scaling), Revolve (a lathe around Y, for bottles and bowls), Sweep (along an arbitrary 3D path, for pipes and cables), and Loft (a blend between cross-sections at different heights). For relief from an image (terrain, lithophanes) there is also Heightmap.
How do I create 3D text?
Build 2D outlines with Text, then extrude:
yaml
render:
op: Extrude
with:
height: 0.05
input:
op: Text
with:
text: "Hello"
fontSize: 0.3
alignX: center
alignY: centerCustom fonts: import a .ttf/.otf into your bundle. Without a font:, a default font is used.
How does the 2D SVG plane map into 3D space?
The SVG plane becomes the 3D X-Z plane (the floor): SVG +X → 3D +X, SVG +Y → 3D +Z. Extruding along +Y lifts profiles off the floor. One SVG user unit is 1 meter internally. On export, dimensions are written in millimeters so vector editors show the expected physical sizes.
Performance & Troubleshooting
- Why is my file slow to run?
- How does caching work?
- What can I do about memory limits?
- What if a run hangs or the tab freezes?
- The 3D view went black or disappeared
- The Design agent says "Thinking…" and never responds
- The viewer shows an old result after I edited
- What hardware and browser do I need?
- How do I report a bug or request a feature?
Why is my file slow to run?
Find the cause before optimizing. While a run is in progress, the status strip in the header shows the current phase and the node being evaluated, so a long stall tells you which operation is the culprit. The usual suspects: a boolean on a high-resolution mesh, a Fillet at fine resolution, a Smooth with many subdivisions, material baking on a complex mesh with large textures, or remote imports being re-fetched at link time (copy them into the bundle).
General rules: lower resolution: on primitives where it doesn't show, prefer group: over Union when you don't need a fused solid, and RoundedBox over Box plus Fillet. Move a suspect part into its own .moon file to measure it alone.
How does caching work?
Any subgraph with identical declaration and inputs is computed once. Re-runs, repeated imports, and repeated use: calls of the same component with the same arguments are nearly free. This is also why determinism is enforced (see the randomness question): the cache must be able to trust that the same inputs mean the same outputs.
What can I do about memory limits?
The Help panel shows the Moon runtime's live memory usage. To reduce the footprint, lower resolution: on primitives, split very large scenes into parts you work on separately, and reduce texture sizes. If the runtime has accumulated too much over a long session, Help → Restart runtime gives it a clean slate. Your files are untouched; only the in-memory cache resets.
What if a run hangs or the tab freezes?
First, Ctrl+C in the terminal cancels a running moon run. If the runtime itself stops responding, Moonomat restarts it automatically and the run ends with an error instead of hanging forever. Long computations are fine: the restart only happens when the runtime stops reporting progress. A single run is capped at 10 minutes, after which it is stopped with an error. If the whole tab is stuck, close and reopen it. Your bundle is saved on every edit, so nothing is lost.
The usual suspects: booleans on very high-resolution meshes, fillets with a radius larger than the thinnest feature, and huge tensor operations.
The 3D view went black or disappeared
On some devices (especially Android) the browser can discard the 3D context under memory pressure. Moonomat detects this and rebuilds the viewer automatically. If a view ever stays black, switching files or reloading the tab restores it. Your design is unaffected; this is purely display-side.
The Design agent says "Thinking…" and never responds
Reload the page. A model stream occasionally gets stuck mid-flight (network blip, provider hiccup). Your session is preserved, and failed requests don't count against your meter.
The viewer shows an old result after I edited
The viewer shows the newest produced output for the selected file. If you edited the source and haven't re-run, you are seeing the previous run. Press Run (Ctrl+Enter).
What hardware and browser do I need?
Any current browser with WebGL2, which is effectively every modern desktop and mobile browser. A laptop with integrated graphics handles scenes intended for web delivery well. All geometry processing runs locally in the browser (WebAssembly), so a faster machine means faster runs.
How do I report a bug or request a feature?
Email support@moonomat.com. For bugs, include what you expected, what happened, a minimal .moon file that reproduces it if applicable, and your browser, OS, and app version (Help → About). Reports that can be reproduced independently get fixed fastest.
Account, Privacy & Billing
- Who owns the content I create with Moonomat?
- Can I use Moonomat output commercially?
- Can I use an embed on a client's site?
- Do you train on my designs?
- What data does Moonomat store about me?
- How do I delete my account?
- What happens if I cancel Full Moon?
Who owns the content I create with Moonomat?
You do. Always.
Can I use Moonomat output commercially?
Yes. There are no royalties and no per-output licensing. Third-party assets you import (textures, models from external libraries) keep their own licenses; respect them as you would anywhere.
Can I use an embed on a client's site?
Yes. You own what you make, including work you make for others, and you can deploy it on their site without a separate license.
You are the only one who needs a plan. Moonomat is priced per person who designs, not per model, per view, or per site. Their visitors never need an account, and nothing is metered by traffic. The Viewer carries a small Moonomat mark; removing it is not currently offered.
Do you train on my designs?
No. We could not even if we wanted to, because your designs are not sent to us. The Design agent runs in your browser and talks directly to the AI provider you configured: your OpenRouter key, a model server on your own machine, or your own Claude subscription through the local bridge. Your files go from your browser to that provider and nowhere else. We train no models on anything.
What data does Moonomat store about me?
In short: authentication identifiers, subscription status, a small request counter for the free Design agent allowance, and pseudonymized analytics. Not your files, designs, prompts, name, or email. Your content lives on your device and in remotes you own. See the Privacy Policy for the complete breakdown.
How do I delete my account?
User menu (bottom of the sidebar) → Account & Privacy → Delete my account, confirmed by typing DELETE. This cancels any active subscription and removes your server-side records. Local files on your device remain until you clear them yourself. Deletion requires being signed in, so only the verified account holder can trigger it.
What happens if I cancel Full Moon?
You keep everything. Every bundle stays local, everything synced sits as plain files in your own Drive and GitHub, and published GitHub embeds keep working. You lose the in-app conveniences: unlimited Design agent use (back to the daily allowance) and the Drive sync surface. Back up anything you care about by pushing it or downloading the packed bundle first, and you can resubscribe anytime.