A Python 3 toolkit that reads, renders and edits Moho project files
(.mohoproj for Moho Pro, .animeproj for Moho Debut, and the .moho /
.anime / .anme ZIP forms) — exporting to SVG and Lottie, importing from
both, and editing documents in place.
Moho's project format is JSON, but undocumented. This tool's behavior was
reverse-engineered by empirically comparing its output against SVG files
Moho itself exported ("File > Export Animation"), across several rigs and
two Moho versions. See
docs/moho-svg-internals.md for the full
reasoning and evidence behind every formula and constant,
docs/moho-python-package.md for the code
layout, and docs/ for shorter, human-oriented guides.
- Export one named layer, every vector layer, or the whole document as one layered SVG.
- Correct handling of Moho's coordinate system, Bezier curve reconstruction, stroke width, tapered (variable-width) strokes, boolean shape combination, masking, bone deformation (rigid and flexible/region binding), and Smart Bones (dial-driven poses).
- Approximates Moho's textured "brush" line styles (stamped dabs with rotation jitter) instead of falling back to a plain uniform stroke, with a choice of render strategies trading off file size/render speed against texture fidelity and vector scalability.
- No required third-party dependencies (stdlib only); Pillow is an optional dependency that unlocks faster brush-texture rendering.
Python 3.9 or newer, and nothing else. Clone the repository and run any script — there is no build step and no installation:
git clone <this repo> && cd Moho2SVG
python3 moho2svg.py MyProject.mohoproj --combined out.svgThat works on a bare Python with zero packages installed. Features that need an optional package say so on stderr and fall back, rather than failing.
One command installs all of them into a local virtualenv:
make venv # creates .venv and installs Pillow, psd-tools, pyclipper, jsonschemaEvery make target then uses .venv automatically — you do not need
to activate it. Activate it only to run a script directly with a bare
python3 ..., which bypasses make:
source .venv/bin/activate| Package | Without it |
|---|---|
| Pillow | Brush-textured documents still export correctly, but the SVG can be very slow to open in a viewer (measured 16.0s vs 2.5s). Recommended. |
| psd-tools | An ImageLayer pointing at a PSD is skipped with a warning. Needs Pillow too. |
| pyclipper | moho2lottie approximates intersect-mode shapes in a way real Lottie players ignore, with a warning. |
| jsonschema | moho2lottie --validate and mohoedit apply-batch's spec validation are skipped. |
Only needed for documents using textured brush strokes — copy them from a local Moho install:
cp -R /Applications/Moho.app/Contents/Resources/Support/Common/Brushes styles/The exporter reads them from styles/Brushes/ by default (--brush-dir
overrides). -R is required: plain cp refuses to copy a directory on
macOS.
| Script | Does |
|---|---|
moho2svg.py |
Moho → SVG |
moho2lottie.py |
Moho → Lottie |
mohoedit.py |
edit a Moho document in place (32 subcommands — see docs/moho-editing-cli.md) |
svg2moho.py |
SVG → Moho |
lottie2moho.py |
Lottie → Moho |
Each takes --help. The code behind them lives in the mohokit package
(layout); these five files are thin
entry points, and import mohokit works from a checkout with no install.
pip install -e . is supported if you would rather have the commands on
your PATH.
# List every layer in the document
python3 moho2svg.py Project.mohoproj --list
# Export one named vector layer
python3 moho2svg.py Project.mohoproj --layer Arm_B --out Arm_B.svg
# Export every vector layer, one file per layer
python3 moho2svg.py Project.mohoproj --all --outdir svg/
# Export the whole document as one layered SVG
python3 moho2svg.py Project.mohoproj --combined Character.svgSee docs/moho-exporting-svg.md for the full flag
reference, typical workflows, and the brush-texture render options
(--brush-dir, --brush-spacing-mul, --brush-raster,
--brush-raster-supersample).
docs/moho-exporting-svg.md— usage guide: every CLI flag, typical workflows, masking quirks, and the brush-texture performance/quality trade-offs.docs/moho-project-file-format.md— a readable summary of the reverse-engineered.mohoproj/.animeprojfile format.docs/moho-animation-and-transform.md— how Moho stores motion (keyframed channels, tweening, actions/Smart Bones) and how the transform stack composes layers, bones and skinning.docs/moho-rigging-and-deformation.md— the bone system in depth (skinning math, binding modes, angle constraints, control bones, IK/target bones, bone dynamics), what Smart Warp leaves behind in the format, and the mesh-level fields that constrain deformation.- The module docstring at the top of
moho2svg.py— the authoritative, evidence-by-evidence source both documents above are distilled from.
moho2svg.py— the tool itself; a single file, no build system.docs/— usage guide and file-format reference (see above).Makefile— pattern rules build any export by name from the command line, e.g.make out/svg/ori/Bandit.svg;make svg-all/make lottie-allbuild every project undermoho/in every export form;make check-lottieandmake check-referenceverify the exporters;make format/moho/Banditpretty-prints one project tomoho/Bandit.json. Runmakewith no target for the full list with examples.moho/,out/,tmp/— gitignored local content (source project files, all svg/lottie export output, scratch notes); not part of the tool itself.styles/Brushes/— Moho's brush textures, copied in locally (see Requirements); tracked with the repository, not part of the tool itself.
This is a best-effort reconstruction of an undocumented file format. Some
formulas are confirmed exact against hundreds of reference samples; others
are best-fit heuristics. See the module docstring's KNOWN GAPS section for
the current list — notably: one boolean shape-combination mode
(combo_mode == 2) is not reverse-engineered, gradient placement is
approximate, the flexible bone-binding weight falloff is unvalidated for
overlapping-influence cases, and textured brush strokes are an approximation
with several further simplifications. PatchLayer is rendered (it
redraws its target layer's mesh at the patch's own point in the draw order),
but by a heuristic that reuses the target's transform instead of the patch's
own — no reference export of a PatchLayer document was available to confirm
it pixel-for-pixel.
Two rigging features are missing rather than approximate: Smart Warp
(distortion-mesh layers — not implemented and not even detected, so the
artwork exports undeformed) and the playback-time bone features
(bone dynamics/spring physics, control bones, IK against a moving target),
whose results are never written into the file's channels. See
docs/moho-rigging-and-deformation.md § 8.
There is no automated test suite. The only way to verify a change is to run
an export against a real .mohoproj/.animeproj file and compare against a
reference SVG Moho itself exported — see
CLAUDE.md for more on the codebase's architecture and
conventions.