Skip to content
munimtechnologiesPublic

About

Write GDevelop 5 project files (scene, sprites, text, behaviors, sounds, JavaScript events) from code, with no editor. By MV2 of Munim, Inc., made with AI agents. Apache-2.0. Not affiliated with GDevelop.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

gdwrite 0.3.1

Write GDevelop 5 project files from code, with no editor open: one scene, Sprite and Text objects, GDevelop's built-in behaviors (such as the platformer ones), instances, image and sound resources and JavaScript code events. png.mjs makes simple sprite images and wav.mjs simple sound effects in code (no dependencies), pixelart.mjs draws pixel-art sprites from text grids and tune.mjs writes music loops, so a whole scene can be generated by a script. harness/ exports a project for the web and plays the build in headless Chromium from a JSON script of steps. Needs Node.js 18 or later.

Licence: Apache-2.0 (see LICENSE). By MV2 of Munim, Inc. (mv2@munimtech.com). The code, tests and this README were written by AI agents (Anthropic Claude) working for MV2, then tested and revised by the agents in an automated sandbox. gdwrite is not made by or affiliated with the GDevelop team.

Version 0.2.0 is also a download (gdwrite-0.2.0.zip) on https://munimv2.itch.io/jamshell, published on 2026-10-18.

Agent skill

gdwrite ships an agent skill, skills/gdwrite/SKILL.md, that tells a coding agent (Claude Code, Codex, Cursor and others that read SKILL.md files) when to use gdwrite, how to set it up, how to write a project, and how to export and play-test it, with the step-script format and the limits. Install it with the open skills command-line tool:

npx skills add munimtechnologies/gdwrite --skill gdwrite

Add --agent claude-code (or another agent name) to pick the agent, and --copy to copy the file instead of linking it. Review a skill before you use it: skills run with your agent's permissions.

What was tested:

  • 2026-11-08, in a sealed node:20 container: tools/skill-check/extract.mjs pulled the skill's example code and step script out of SKILL.md; the example wrote game.json from GDevelop's breakout and platformer examples, harness/export.mjs built it for the web and harness/play.mjs ran the step script in headless Chromium: 3 of 3 steps passed, with no page or console errors (details in tools/skill-check/README.md).
  • 2026-11-09, in a sealed node:22 container: npx skills add munimtechnologies/gdwrite --list found the one skill, and npx skills add munimtechnologies/gdwrite --skill gdwrite --agent claude-code --copy -y installed it to .claude/skills/gdwrite/SKILL.md with the frontmatter intact. That was a single install, made once to check the command works.

New in 0.3.0 (2026-11-05)

  • pixelart.mjs: sprites from text grids. gridPNG(rows, palette, { scale }) turns rows of characters into a PNG (one character per pixel, . transparent, an unknown character throws), sheetPNG(frames, ...) lays equal-size frames side by side, outline(rows, ch) rings the opaque pixels with a 1-pixel outline.
  • tune.mjs: tuneWAV({ bpm, voices }) mixes note lists (MIDI numbers and beats; null is a rest; sine, square, triangle or saw) into a WAV loop with click-free note edges; noiseWAV() makes a seeded, decaying noise burst for hits and steps.
  • harness/export.mjs: exports a project for the web without the editor (needs gdcore-tools).
  • harness/play.mjs: plays an exported build in headless Chromium (needs playwright) from a JSON script of steps (wait, click, tap, drag, key down / up / press / hold, eval with expect, wait_for, screenshots, repeat) and writes a JSON report. It exits 0 only if the page made a canvas, threw no page error and every check passed. harness/expect.mjs holds the comparison (eq, ne, gt, gte, lt, lte, between, truthy, includes).
  • build-artprobe.mjs and steps/*.json: probe scenes and their scripts (steps/negative.json must fail, to show the harness reports failures).
  • npm test now runs all three test files.
npm install gdcore-tools@2.0.0-gd-v5.6.269-autobuild playwright@1.47.0
npx playwright install --with-deps chromium
node build-demo.mjs demo breakout.json platformer.json
node harness/export.mjs demo/game.json build-demo
node harness/play.mjs build-demo steps/demo.json out

How it works

GDevelop's project format has many fields that change between versions. gdwrite does not hard-code them: loadTemplates() reads one or more example projects that GDevelop itself saved (for example the official GDevelop examples) and clones a scene, a layer, a Sprite, a Text object, an instance, an image resource and every behavior it finds from them. You then fill in only what you want: names, images, sounds, behavior settings, positions and event code.

import fs from "node:fs";
import { loadTemplates, behaviorTypes, spriteObject, textObject, withBehavior, imageResource, audioResource,
  instance, jsEvent, buildProject } from "./gdwrite.mjs";
import { shapePNG } from "./png.mjs";
import { blipWAV } from "./wav.mjs";

const t = loadTemplates(["breakout.json", "platformer.json"].map((f) => JSON.parse(fs.readFileSync(f, "utf8"))));
console.log(behaviorTypes(t)); // the behavior types the examples contain
fs.writeFileSync("player.png", shapePNG(32, 32, [80, 170, 255], "box"));
fs.writeFileSync("ground.png", shapePNG(32, 32, [90, 200, 120], "box"));
fs.writeFileSync("blip.wav", blipWAV({ f0: 520, f1: 880, ms: 120 }));
const project = buildProject(t, {
  name: "my game", packageName: "com.example.mygame", width: 800, height: 600, bg: [24, 40, 72],
  resources: [imageResource(t, "player.png"), imageResource(t, "ground.png"), audioResource(t, "blip.wav")],
  objects: [
    withBehavior(t, spriteObject(t, "Player", "player.png"), "PlatformBehavior::PlatformerObjectBehavior", "PlatformerObject"),
    withBehavior(t, spriteObject(t, "Ground", "ground.png"), "PlatformBehavior::PlatformBehavior", "Platform"),
    textObject(t, "Hud", { text: "Hello" }),
  ],
  instances: [instance(t, "Player", 100, 100, 1), instance(t, "Ground", 80, 400, 2, 1, { width: 640, height: 32 }), instance(t, "Hud", 24, 24, 3)],
  events: [jsEvent("if (runtimeScene.getObjects('Player')[0].getBehavior('PlatformerObject').isJumping()) {}")],
});
fs.writeFileSync("game.json", JSON.stringify(project, null, 2));

withBehavior(t, object, type, name, props) only accepts properties the example's behavior already has, so a misspelt setting fails when the project is written, not later in the engine. The scene's behavior shared data is filled in for you. A sound is played from a JavaScript event with gdjs.evtTools.sound.playSound(runtimeScene, "blip.wav", false, 60, 1).

Complete examples:

  • node build-demo.mjs <out dir> <example.json> [...]: a player moved with the arrow keys, coins spawned at runtime, collisions and a score display.
  • node build-platformer.mjs <out dir> <example.json> [...] (needs an example with the platformer behaviors, such as GDevelop's "platformer"): a player with the Platformer character behavior on a stretched ground with the Platform behavior, GDevelop's default controls, and a blip on landing.
  • node build-artprobe.mjs <out dir> <example.json> [...]: a pixelart sprite drawn at 4x, a tune.mjs music loop and a noise hit sound.

The example projects are not included; get them from https://github.com/GDevelopApp/GDevelop-examples (examples/breakout/breakout.json and examples/platformer/platformer.json were used in the tests below).

A step script is a JSON object with viewport, touch (for tap steps), boot_ms and a list of steps (the full list of step kinds is at the top of harness/play.mjs). Each step with expect is compared with harness/expect.mjs; a game exposes its state on a global (the probes use window.__gdprobe) so the script can read it:

{
  "viewport": { "width": 800, "height": 600 },
  "boot_ms": 8000,
  "steps": [
    { "wait_for": "window.__gdprobe && window.__gdprobe.frames > 30", "timeout_ms": 20000 },
    { "hold": "ArrowRight", "ms": 2500 },
    { "eval": "window.__gdprobe.px", "save": "x_after", "expect": { "gt": 200 } },
    { "shot": "after" }
  ]
}

What was tested

  • 0.3.1 changes only this README, the changelog and the version in package.json; the code is the same as 0.3.0. The agent-skill checks are listed under "Agent skill" above.
  • 0.3.0, in an automated sandbox on 2026-11-05 (Node 20.20.2, gdcore-tools 2.0.0-gd-v5.6.269-autobuild, Playwright 1.47.0 with Chromium in software rendering):
    • node --test test_gdwrite.mjs test_art.mjs harness/test_expect.mjs: 28 tests, 28 passed (16 for gdwrite, PNG and WAV; 8 for pixelart and tune; 4 for the expect comparisons).
    • The three probe scenes were built with templates from GDevelop's breakout and platformer examples, exported for the web with harness/export.mjs and played with harness/play.mjs, with no page errors and no console errors: coin demo 7 of 7 checks passed (the player moved from x = 40 to x = 570 on a held arrow key, a coin was collected, coins spawned at runtime); platformer 11 of 11 (the player landed on the ground, walked right to x = 333, jumped once rising 157 px, landed again, and the blip sound played without an error); art probe 4 of 4 (the 16 x 16 grid sprite drawn at 64 x 64, the music loop was started once and the noise hit sound was played at least twice, with no error caught; headless Chromium has no speakers, so nobody listened to the sounds).
    • steps/negative.json failed as it should: 0 of 3 checks passed (an impossible score, a wait_for that never comes true and an expression that throws) and play.mjs exited 1.
  • 0.2.0: in an automated sandbox on 2026-10-18 (Node 20), all 16 tests passed on these files, both from the source folder and again from a clean unzip of gdwrite-0.2.0.zip, and both example scenes were exported and played in headless Chromium with no page errors. The project built from the clean unzip matched the tested one byte for byte.
  • gdwrite 0.1.0 output also opened in the GDevelop 5.6.284 desktop editor with no warning. 0.3.0 output has not yet been opened in the desktop editor.
  • The step that commits these files to GitHub does not run Node, so the tests were not re-run at commit time; run npm test yourself.

Limits

  • One scene; Sprite and Text objects; image and sound resources; JavaScript events. No regular (non-code) events, fonts, tilemaps or physics yet.
  • Behaviors must be ones GDevelop ships with and must appear in an example you supply. Behaviors from custom extensions are not copied, because the extensions themselves are not.
  • Fonts are left empty, so text uses GDevelop's default font.
  • Templates come from projects you supply; fields GDevelop adds in later versions are whatever those projects contain.
  • The play harness was tested at 800 x 600 in software-rendered Chromium only; timings in a step script (holds, waits) depend on the machine, so give checks some slack.

Prior art

Other people have built tools for working with GDevelop projects from code, among them GDevelop's own command-line tools, gdcore-tools / gdexporter (exporting without the editor, used in the tests above) and MCP bridges that let AI assistants edit GDevelop projects. gdwrite is a small, dependency-free alternative for writing a project file from scratch; it is not the first tool of its kind. Headless play-test scripts for browser games are common too (Playwright itself, and many game test runners); harness/play.mjs is a small one tailored to exported GDevelop builds.

About

Write GDevelop 5 project files (scene, sprites, text, behaviors, sounds, JavaScript events) from code, with no editor. By MV2 of Munim, Inc., made with AI agents. Apache-2.0. Not affiliated with GDevelop.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages