MagicPixel
MarketplaceGuidesPricingRewards
  1. Guides
  2. Sync to your game
Sync guide & referenceBeta

Sync MagicPixel art to your game

One prompt for web projects, or a short CLI flow for Unity, Godot, and GameMaker. Sprites land as PNGs on disk and stay in sync as you edit. Full CLI and REST API reference is below.

Setup

start, then sync --watch

Updates

Auto (~2s with Vite plugin)

Files land in

outDir

Works with

Vite, Unity, Godot, GameMaker, …
Edit in MagicPixel
Vite plugin syncs
Browser hot-reloads

~2 seconds, no terminal

Contents

  • Quickstart (AI + Vite)
  • Game engines
  • Manual setup (web)
  • magicpixel.json config
  • CLI commands
  • REST API
  • Troubleshooting
  • Telemetry

Quickstart (AI + Vite)

Before you paste it

  • • A MagicPixel account (you're here ✓)
  • • A few sprites saved in your library
  • • A game project open in an AI coding tool

Prompt preview

Preview only — the key is a placeholder. Make a real key in step 2 and use its Copy setup prompt button.

Set up MagicPixel asset sync. The Vite plugin pulls my saved sprites into the project as PNGs and keeps them updated automatically while `vite dev` runs — dev-time only, never called from app runtime.

Verify on npm first (if either fails to resolve, stop and tell me — don't substitute look-alike packages):
- https://www.npmjs.com/package/@magicpixelart/vite
- https://www.npmjs.com/package/@magicpixelart/cli
- Docs: https://magicpixel.art/guides/sync

Then do these in order and report back:

1. Save the value below as a secret named MAGICPIXEL_API_KEY (use the project's secret manager — do not commit it or write it to .env):

   mp_live_paste_your_key_here

2. Install @magicpixelart/vite as a dev dependency (it pulls in @magicpixelart/cli).

3. Add the plugin to vite.config.ts:

   import { magicpixel } from '@magicpixelart/vite';
   export default defineConfig({ plugins: [/* existing */, magicpixel()] });

4. Run `npx @magicpixelart/cli start` with MAGICPIXEL_API_KEY in the environment. This authenticates, creates magicpixel.json, and syncs sprites into src/assets/magicpixel/.

5. Add `.magicpixel/` to .gitignore, then restart `vite dev`. Future saves in MagicPixel hot-reload in ~2 seconds.

6. Tell me how many sprites synced and the top-level folders they landed in.

Note: the key above ("mp_live_paste_your_key_here") is a placeholder. Ask me for my real key (starts with mp_live_) from https://magicpixel.art/settings → API Keys before running anything.
1

Organize your sprites

Group related sprites into folders — the folder + sprite name becomes the file path your game code uses.

player/
  rabbit-idle.png
  rabbit-run.png
enemies/
  slime.png
ui/
  heart.png

Working on more than one game? Right-click in the library → New project, then bind your sync key to it in step 2.

2

Create your sync key

  1. Open Settings → API Keys.
  2. Click Create Key (pick a project if you have several).
  3. Use the Copy setup prompt button next to the new key.

Revoke and replace anytime from Settings. Production keys are prefixed mp_live_.

3

Paste the prompt into your AI tool

Paste into Lovable, Bolt, Cursor, or Replit. Your tool saves the secret, installs the plugin, and runs the initial sync. Sprites land in src/assets/magicpixel/.

Closed the key panel? Revoke that key and create a new one — it's one click.

After it's working

  • Use a sprite: render from src/assets/magicpixel/<folder>/<name>.png — pixel-perfect, no smoothing.
  • Typed imports: import from @/assets/magicpixel to get a typed MagicPixelAssets map (and MagicPixelAssetsById that survives renames).
  • Update later: save here, browser hot-reloads in ~2s. No commands.

Game engines (Unity, Godot, GameMaker)

The CLI detects your engine from marker files in the project root and picks a sensible outDir automatically. No Vite plugin, no package.json, and no typed index.ts — just PNGs where your engine expects them.

EngineDetected whenDefault outDir
Godotproject.godotassets/magicpixel
UnityProjectSettings/ProjectVersion.txtAssets/MagicPixel
GameMaker*.yypdatafiles/magicpixel

Unity: how syncing works

CLI watcher (any browser, any OS). Run npx @magicpixelart/cli start in your Unity project folder, then leave npx @magicpixelart/cli sync --watch running. That is the whole loop: game sprites import into Connected, and every save writes back to the original PNG. Also the right choice for CI and for teammates sharing one project. The walkthrough lives in Settings → Unity sync.

Nothing syncs until you pick it. Only artboards selected by a Sync to Game press land in Assets/MagicPixel, so 100 drafts can live in a document while just the three finished sprites sync. A connected folder determines where those selected sprites are written; it never selects every artboard in a file. Previously downloaded artboards that are no longer selected are removed on the next full reconcile, while unrelated game-authored files remain protected. If nothing is selected, the CLI skips the pull.

Nothing leaves the editor until you press Sync, and a press covers one artboard. The Sync button sits in that artboard's top-right corner and releases that sprite alone — pressing it exclusively marks the artboard, clearing any older selection in that file, so everything else in the same document (variants, pasted scratch, experiments) stays behind even when the file lives in a connected folder. The label reads Synced once it has landed. Clicking Sync on a different artboard switches the file's export to that artboard only.

Need a few at once? Select the artboards you want, then choose Sync N Artboards to Game from the artboard ⋮ menu (or right-click). That press behaves the same way: exactly the selected artboards are released and every other artboard in the file stops exporting.

Synced a file before you narrowed it down? Run npx @magicpixelart/cli sync --full once — the full pass removes sprites your project no longer asks for (and their .meta files). The periodic full reconcile does the same on its own.

Your library folders become Unity folders. Synced assets mirror the folder tree you organized in the library, so Props/Rocks/rock-a in MagicPixel writes to Assets/MagicPixel/Props/Rocks/rock-a.png. Rename a folder in MagicPixel and the next sync moves the files rather than duplicating them.

Two-way with sync --watch. Leave it running. Saves in Connected overwrite the original game files a couple of seconds later; edits to those files come back into MagicPixel on the same tick. npx @magicpixelart/cli push is upload-only if you want that without a pull. Start with push --dry-run to see the list first. Multi-layer or animated artboards are skipped unless you pass --flatten, which replaces them with the flat image on disk.

If a sprite changed in both places, push reports a conflict and writes nothing, so neither copy is lost. You pick the winner: press Sync on that artboard to take MagicPixel's version for that one sprite, or run push --force to keep what is on disk. Either way your other files are untouched, and --force still re-checks the live cloud copy first, so a save that lands mid-push is reported rather than silently overwritten.

Both paths write pixel-art-correct .meta sidecars — point filtering, no compression, and deterministic GUIDs, so scene and prefab references never break. Existing .meta files are left untouched, so importer tweaks you make in Unity survive re-syncs. Set "unityPpu" in magicpixel.json to change pixels-per-unit (default 32).

Run these from your game project root (where the marker file lives). The package is @magicpixelart/cli and the command it installs is magicpixel:

npx @magicpixelart/cli@latest start
npx @magicpixelart/cli sync --watch

start creates the config, logs you in, and runs the first sync (including your game sprites). Always pass the full package name: magicpixel is the binary the package installs, not a package on npm, so the short magicpixel … form only works after npm i -D @magicpixelart/cli in that project. The first npx run asks to install the package — answer yes, or use npx -y @magicpixelart/cli … in CI.

  • Godot & Unity: PNGs auto-import when files change — leave sync --watch running in a terminal while you edit in MagicPixel.
  • GameMaker: files land in Included Files, but the IDE does not auto-refresh — use Refresh Included Files after each sync (the CLI reminds you at init time).
  • Verify setup: npx @magicpixelart/cli doctor --json reports "framework": "Godot" (or Unity / GameMaker) and your outDir.

Run from your game project folder

Always run sync inside the folder you connected (the one with your sprites). Sprites from another game folder are skipped instead of being copied into Assets/MagicPixel. Add --here only if you really want copies in this folder. Run magicpixel clean-strays to remove copies an older version left behind, and magicpixel why <folder> to see why a file isn't uploading.

Several game folders in one repo

Run npx @magicpixelart/cli@latest start in the folder that holds them all. It keeps one setup there, merges any older per-folder setups (backed up first), and scans every game folder, including hidden ones like .SpineRaw. Commands run inside a game folder use that one setup automatically. Merging also tidies the watched-folder list (removes duplicates and broken entries).

A folder isn't syncing

Run npx @magicpixelart/cli why .SpineRaw/rabbit. If it says "not watched", run the connect command it prints, e.g. npx @magicpixelart/cli connect '.SpineRaw/rabbit/**'. That pattern doesn't include sibling folders like rabbit_nft. To keep a folder out on purpose, add it to exclude in magicpixel.json. Run npx @magicpixelart/cli --help for all common tasks.

To sync only some folders

By default Unity, Godot, and GameMaker sync every game PNG. npx @magicpixelart/cli connect 'Sprites/Hero/**' narrows that. To watch just one folder of a big project, run npx @magicpixelart/cli sync --watch --only Sprites/Enemies. Everything outside that folder is left untouched.

Resync one folder from your game

Changed a lot of files locally (for example, resized them)? Run npx @magicpixelart/cli resync 'Sprites/Enemies' --dry-run to preview, then again without --dry-run. Or right-click the folder in the Library and choose Resync from game… while sync --watch runs. Local files win: MagicPixel copies are overwritten and resized, and files missing locally move to Trash. Trash works per file: an artboard inside a file is never deleted, even if its frame is gone locally.

Use sprites in-engine via each engine's normal PNG path (e.g. Godot res://assets/magicpixel/…, Unity Assets/MagicPixel/…). For Godot and GameMaker the CLI leaves sidecar metadata (.import, .yy) to the engine on first import; for Unity both the CLI and the in-app sync write tuned .meta files for you.

Manual setup (web projects)

For Vite / React / Next and other JavaScript projects — without the AI quickstart prompt. Game engines: see Game engines.

Option A — Vite plugin (recommended)

npm i -D @magicpixelart/vite

The plugin bundles @magicpixelart/cli. Add it to your Vite config:

// vite.config.ts
import { defineConfig } from 'vite';
import { magicpixel } from '@magicpixelart/vite';

export default defineConfig({
  plugins: [/* your existing plugins */, magicpixel()],
});

Then authenticate once:

npx @magicpixelart/cli start

start detects your framework, writes magicpixel.json, prompts for your API key, runs the first sync, and offers a magicpixel:watch npm script. After that, keep starting your game normally. The plugin runs the watcher with your dev server, so every artboard you explicitly Sync in MagicPixel arrives within ~2 seconds — no sync command after each change. Vite HMR picks it up automatically.

Option B — CLI only (non-Vite, CI)

npm i -D @magicpixelart/cli
npx @magicpixelart/cli start                # one-time auth + initial sync
npx @magicpixelart/cli sync --watch         # keep assets fresh while you work

start stores your key in .magicpixel/credentials (mode 0600, auto-gitignored). For CI, set MAGICPIXEL_API_KEY in the environment instead — it takes precedence over the credentials file.

magicpixel.json config

Lives at the project root. Edit by hand or via magicpixel add <glob> / magicpixel remove <glob>.

{
  "outDir": "src/assets/magicpixel",
  "include": ["rabbit/**", "ui/icons/*"],
  "exclude": ["**/wip-*"],
  "emitIndex": true
}

init detects your project kind and suggests outDir. Defaults:

ProjectDefault outDirNotes
Vite, Remix, TanStack Startsrc/assets/magicpixel—
Next.js, Astro, Nuxt, CRApublic/magicpixel—
SvelteKitstatic/magicpixel—
Godotassets/magicpixelres://assets/magicpixel/…
UnityAssets/MagicPixelauto-imports under Assets/
GameMakerdatafiles/magicpixelIncluded Files — refresh after sync
Unknown / otherassets/magicpixelsrc/assets/magicpixel if src/ exists
ParameterTypeRequiredDefaultDescription
outDirstringNoproject-kind defaultWhere PNGs (and index.ts when emitIndex is on) are written.
includestring[]No["**/*"]Picomatch globs matched against "folder/slug".
excludestring[]No[]Globs to skip both ways: never downloaded, and matching game PNGs are never uploaded (wins over connect). To hide a folder, add e.g. ".SpineRaw/rabbit_nft/**", then delete it in the library — the files stay in your game.
connectstring[]No["**"] on Unity/Godot/GameMakerGame PNG globs to keep in Connected. Default is all sprites; narrow with magicpixel connect.
syncOnlystring[]No—Default for sync --only: game folders a plain sync is limited to.
emitIndexbooleanNotrue (JS only)Emit <outDir>/index.ts with typed asset map. Off for Unity/Godot/GameMaker.
endpointstringNoproductionOverride the API base (must be HTTPS). Testing only.

State (.magicpixel/state.json) tracks lastSync. Add .magicpixel/ to .gitignore (init offers to do this).

CLI commands

CommandWhat it does
start [--force]Bootstrap: init + login + first two-way sync. Works for Unity, Godot, GameMaker, and JS.
init [-y] [--force]Config wizard. Detects Vite/Next/Unity/Godot/GameMaker and suggests outDir.
login [--key <key>]Save your API key to .magicpixel/credentials. Validates against the server first.
logoutRemove the stored API key.
sync [...flags]Two-way: pull MagicPixel edits and push changed game sprites. Use --watch while you work.
push [--dry-run] [--flatten] [--force]Upload-only: send local PNG edits without pulling. --force keeps your local copy on a both-sides-changed conflict.
resync <folder> [--dry-run] [-y]Local files win for one folder: overwrite MagicPixel copies, move cloud-only files to Trash.
connect <glob>Limit which game folders sync (default is all sprites).
why <path>Explain why files in a folder aren't uploading, and print the connect command that fixes it.
clean-strays [--dry-run] [-y]Remove copies sync wrote into the wrong game folder. Your own game files are never touched.
consolidate [--dry-run]Merge per-folder setups in a monorepo into one root setup (backed up first).
search <query>Search indexed game PNGs (no network).
add <glob> / remove <glob>Manage include patterns for MagicPixel → disk.
listPrint the matching manifest as a table.
statusConfig, last sync, diff vs remote.
whoamiVerify API key, report visible assets.
doctor [--json] [--offline]One-screen diagnostic. Paste it to your AI agent when something breaks.
repair [--dry-run] [-y]Self-heal: validate key → quarantine state.json → prune empty dirs → full re-sync.

sync flags

FlagMeaning
-w, --watch [seconds]Poll for changes (default 2s; auto-slows to 5s after ~1min idle, 10s after ~5min, 30s after ~15min — and snaps back instantly on the first change).
--no-pruneKeep local files not in the manifest (default: prune them on full syncs).
--dry-runPrint plan, write nothing.
--fullIgnore lastSync; re-fetch the full manifest.
-c, --concurrency <n>Parallel downloads (1–16, default 6).
-q, --quietMinimal output (for CI).
--only <folder...>Only push, pull and prune inside these game folders (repeatable). Everything else is left untouched.

Each successful sync prints a per-file change list (+ added, ~ modified, ↪ renamed, - pruned). sync --watch exits 2 after 5 consecutive auth failures so process supervisors can detect a revoked key.

REST API (advanced)

The CLI is built on a small REST API — reads for pulling sprites down, plus a write-back endpoint for pushing disk edits home. Use it directly when you can't run Node (game-engine plugins, scripts in other languages, etc.).

Authentication

All requests need an API key in the Authorization header. Create keys in Settings → API Keys.

Authorization: Bearer mp_live_abc123...

Each key is bound to a single project and only sees that project's assets. Revoke anytime from Settings.

Base URL

https://sddsilidjhvtvejzvolx.supabase.co/functions/v1

List assets (manifest)

GET/integration-assets/manifest

List the assets your key can see. Designed for diffing against disk before downloading only what actually changed.

Query parameters

ParameterTypeRequiredDefaultDescription
includestring[]No—Globs matched against "folder/slug". Repeatable. Default: match all.
excludestring[]No—Globs to exclude. Repeatable.
sinceISO8601No—Only return assets updated after this timestamp.
cursorstringNo—Opaque pagination cursor from the previous response.
limitnumberNo500Max items per page (1–1000).

Response (200 OK)

{
  "items": [
    {
      "id": "e5f6a7b8-...",
      "key": "rabbit/ear-left",
      "name": "Ear Left",
      "folder": "rabbit",
      "slug": "ear-left",
      "width": 64,
      "height": 64,
      "sha256": "a1b2c3...",
      "size_bytes": 482,
      "updated_at": "2025-01-15T12:00:00Z",
      "download_url": "https://sddsilidjhvtvejzvolx.supabase.co/functions/v1/integration-assets?key=rabbit%2Fear-left"
    }
  ],
  "nextCursor": "MjAyNS0w...",
  "count": 1
}

When nextCursor is non-null, pass it back as ?cursor= to fetch the next page. Compare sha256 against the version on disk to decide whether to re-download.

Download asset

GET/integration-assets?key=folder/slug

Download an asset as a raw PNG. Address it by stable key (recommended) or by assetId.

Query parameters

ParameterTypeRequiredDefaultDescription
keystringNo—Stable key — "folder/slug" or just "slug". Recommended.
assetIduuidNo—Asset UUID. Either key or assetId is required.

Example

# Address by key (recommended):
curl -o ear-left.png \
  -H "Authorization: Bearer mp_live_abc123..." \
  "https://sddsilidjhvtvejzvolx.supabase.co/functions/v1/integration-assets?key=rabbit/ear-left"

# Or by asset ID:
curl -o sprite.png \
  -H "Authorization: Bearer mp_live_abc123..." \
  "https://sddsilidjhvtvejzvolx.supabase.co/functions/v1/integration-assets?assetId=e5f6a7b8-..."

Save bandwidth with ETags

Every PNG response includes an ETag equal to the asset's sha256. Send it back as If-None-Match on subsequent requests to get a 304 Not Modified when nothing changed. The CLI uses this on every poll, so a no-op watch tick downloads zero PNG bytes.

Write sprites back (beta)

POSThttps://magicpixel.art/api/public/integration/ingest

Push a PNG edited on disk back into MagicPixel — the write half of two-way Unity sync. Send up to 20 sprites per request (16 MB of PNG data total, 4096px per side). Each sprite either updates a known artboard (assetId + layerIdx) or adopts a new file into the library by its disk path (path). Sprites that target the same document are applied together in one write.

Body parameters (per sprite)

ParameterTypeRequiredDefaultDescription
keystringYes—Your identifier, echoed back in the matching result.
pngBase64stringYes—Base64 PNG bytes for the sprite as it exists on disk.
diskSha256stringYes—sha256 of the disk PNG; identical bytes short-circuit to unchanged.
assetIduuidNo—Document to write into. With layerIdx, this is an update.
layerIdxnumberNo—Artboard index inside that document (required with assetId).
baseSha256stringNo—Sha the file was last synced from. Required for updates — a mismatch is refused as cloud-changed.
baseVersionnumberNo—Manifest version the file was last synced from. When it still matches, the update is accepted even if the sha differs (only content saves raise the version).
pathstring[]No—Slug path for adoption, e.g. ["props","rocks","rock-a"]. Folders are created as needed.
pathNamesstring[]No—Display names matching path, so disk casing survives folder creation.
flattenbooleanNofalseAllow overwriting a multi-layer or animated artboard with one flat PNG.

Response (200 OK)

{
  "results": [
    { "key": "props/rock-a", "status": "updated", "assetId": "e5f6...", "layerIdx": 1, "sha256": "a1b2..." },
    { "key": "props/rock-b", "status": "unchanged", "assetId": "e5f6...", "layerIdx": 2, "sha256": "c3d4..." },
    { "key": "props/rock-c", "status": "conflict", "reason": "cloud-changed", "assetId": "e5f6...", "layerIdx": 3, "sha256": "9f8e..." }
  ]
}

Per-sprite status is updated, created, unchanged, conflict, or error — the request itself still returns 200, so read every result. Conflict reasons: cloud-changed (someone saved in the editor — a cloud-changed result carries the live sha256, so you can re-send with it as baseSha256 to keep your copy), would-flatten (layered or animated artboard; pass flatten to accept the flattening), legacy-document, and not-found. A lost conflict never writes: your cloud document is left byte-for-byte intact.

The CLI does this for you

npx @magicpixelart/cli push walks your synced folder, hashes every PNG, and calls this endpoint with the right shape — including the conflict baseline. Use the raw endpoint only for plugins and scripts.

Error handling

All error responses use a consistent JSON shape:

{
  "error": "Human-readable error message"
}

Every response also carries an X-Request-Id header — include it when reaching out for support and we can grep the edge logs in one shot.

StatusMeaning
304Not Modified — ETag matched (asset downloads only).
400Bad request — invalid or missing parameters.
401Unauthorized — missing or invalid API key.
403Forbidden — key is valid but lacks access to the requested asset.
404Asset not found in this project.
405Method not allowed.
429Rate limit / daily download cap exceeded — retry after a delay; CLI auto-backs off.
5xxServer error — CLI retries with backoff and reports the request id.

Troubleshooting

Most issues are diagnosable in one command: npx @magicpixelart/cli doctor (add --json for a machine-readable report, --offline behind strict proxies). For a clean-slate recovery, run npx @magicpixelart/cli repair --dry-run first to preview, then drop the flag.

I'm using Unity, Godot, or GameMaker — where do I start?

From your game project root: npx @magicpixelart/cli start, then npx @magicpixelart/cli sync --watch. See the Game engines section for default folders. After npm i -D @magicpixelart/cliyou can type the short magicpixel sync --watch.

I want several artboards to sync at once.

Select the artboards, then choose Sync N Artboards to Game from the artboard ⋮ menu. Connected folders organize their destination; they never select every variant automatically.

Something's broken — where do I start?

Ask your AI tool: "Run npx @magicpixelart/cli doctor and tell me what it says." It pinpoints the API key, project binding, network, or stale CLI in one shot. If you need to share it with us, magicpixel doctor --json | jq has a stable schema and no ANSI codes.

My AI tool says the key is invalid.

The key probably wasn't saved as a secret. Ask: "Confirm MAGICPIXEL_API_KEY is set, then run npx @magicpixelart/cli doctor."

Nothing got downloaded.

Either no sprites are saved yet, or your key is bound to a project that has none. Check Settings → Projects, or run npx @magicpixelart/cli whoami.

I only want one folder, not everything.

For game sprites, run npx @magicpixelart/cli connect 'Sprites/Hero/**' or edit connect in magicpixel.json. For MagicPixel → disk, edit include or use npx @magicpixelart/cli add 'player/**'.

I renamed or deleted a sprite, but the old file is still there.

Pruning is on by default — a re-sync removes it. Pass --no-prune if you want to keep stale files. If things still look out of sync, npx @magicpixelart/cli repair wipes local state and runs a full re-sync.

Sync was working, then started failing.

Most likely today's per-key download quota — it resets at UTC midnight, or upgrade your plan to raise the cap. Transient 429/5xx failures auto-recover with backoff.

The index.ts file doesn't reflect a rename.

Run npx @magicpixelart/cli sync --full once. Renames are detected against the previous sync's snapshot; a full pass guarantees the new key lands. Prefer MagicPixelAssetsById['…'] for assets you don't want to chase imports for — it survives every rename.

Files keep getting re-downloaded every sync.

Your build system is rewriting the PNGs on disk, so the sha256 no longer matches. Sync into a directory your bundler reads but doesn't mutate (avoid putting them in a folder an image optimizer touches).

Telemetry

Unexpected CLI failures (5xx server errors, uncaught exceptions) are reported fire-and-forget so we can fix issues before you have to file them. We send: the error message + stack, command name, CLI version, Node version, OS platform, and the request id from the failed call. We never send file paths, asset names, configuration, environment variables, or your API key.

Opt out with MAGICPIXEL_TELEMETRY=0. Reporting is also automatically skipped when no API key is configured or when endpoint in magicpixel.json points at a non-canonical host.

Open the editorAll guidesCLI on npm

Related guides

  • MarketplaceWhat buyers expect, the 80% creator revenue split, normalization rules, and tips for getting your submission accepted.
  • Generating game spritesFrom a one-line idea to a game-ready sprite: artboard sizes, prompt structure, references that lock style, and expanding one sprite into sets and sheets.
  • Editing AI pixel artThe cleanup pass: fix outlines, strip leftover backgrounds, snap to a palette, and run targeted AI edits on a selection instead of redrawing the sprite.
© 2026 MagicPixelAI pixel art generatorPixel art editorMarketplaceGuidesPalettesPricingAboutRewardsConnectPrivacyTerms