Design stunning 2D & 3D particle effects visually — and play them in GDevelop.
A standalone desktop editor with real-time GPU preview, paired with the Advanced Particle Emitter extension for GDevelop.
⬇️ Download · 🎮 itch.io · ✨ Features · 🚀 Quick Start · 🎨 Blend Modes · 📚 Docs · 🛣️ Roadmap
📌 Which version does this describe? This README documents the released v0.2.0 editor and extension. Anything marked 🚧 is already in
mainbut not in a release yet — see Unreleased. The next release is planned once the viewport migration roadmap is complete.
Lempo Particle FX is a two-part toolkit:
| Part | What it is |
|---|---|
| Lempo Particle Editor | A Dear PyGui desktop app (LempoParticleEditor.exe) for designing effects with a live viewport, then exporting them as JSON. |
| Advanced Particle Emitter | A GDevelop extension (AdvancedParticleEmitter.json) that renders those effects in-game: 2D via PixiJS and 3D via Three.js. |
Both parts share the export schema and named contracts (see Shared Contracts). The editor viewport, the browser preview and the GDevelop extension are separate runtimes, so rendering and simulation details can differ between them. The GDevelop extension keeps its own simulation implementation, so a few behaviors are not yet record-identical across the preview and the game runtime; see the viewport migration notes.
Works in any GDevelop project — design the effect in the editor, play it in-game with the Advanced Particle Emitter extension.
![]() 3D viewport |
![]() 2D viewport |
![]() 3D browser fast preview |
![]() 2D browser fast preview |
![]() Emitter panel |
![]() Per-state colors (3D) |
![]() Trails & Ribbons (2D) |
![]() Trails & Ribbons (3D) |
- 12 particle shapes — circle, square, triangle, star, diamond, line, custom, sphere, cube, pyramid, torus, billboard — shared by the 2D and 3D renderers.
- Custom 3D models & images — upload
.glb/.gltf/.obj(or images for 2D). Models render as themselves in the viewport, the browser preview, and in-game. Rigged/skinned GLBs are baked to their rest pose automatically. - Per-state colors — birth, death and any extra states you add each keep their own color. White means natural materials; any other color tints over the base.
- Gradual shape morph — birth-to-death shapes cross-fade inside the
(0.25, 0.75)window instead of snapping (desktop viewport + browser preview; game runtime keeps the classic swap for meshes). - Ready-made templates — Explosion, Fire, Rain, Snow (2D + 3D variants), with 100-step undo/redo.
- Force fields (export v1.2) — age-phased turbulence, Y-axis vortex, linear-falloff attractor, and a bounce/friction collision plane. All off by default (legacy motion stays bit-identical).
- Deterministic seed — a nonzero
seedmakes replay deterministic within each runtime;0keeps legacy unseeded behavior. Identical seeds are not guaranteed to produce identical particle records across the editor, the browser preview and GDevelop. - Blend modes — Normal, Additive, Subtractive, Multiply, Screen, Lighten, Overlay, selectable per emitter (see Blend Modes).
- Trails & Ribbons — a dedicated mode (key
4for 2D, key5for 3D in the startup dialog) with a Unity-style inspector, width/gradient curve editors,time/distanceemission, and a browser of 27 ready-made templates (combat, magic, movement, nature, stylized) with search, favorites, and your own saved presets. 👉 Full details: docs/trails.md
The Dear PyGui sidebar exposes blend, seed, and force-field widgets. The Tk fallback preserves those values on round-trip but has no widgets for them.
- InstancedMesh batching (3D primitives) — ~580 draw calls collapse into ~12 buckets, verified pixel-identical per particle (matrix, color, alpha), including morph flips and all blend modes.
- Lazy buckets & pooled runtime — empty buckets cost zero draw calls, and shape-aware mesh/material pooling keeps steady state free of per-frame allocations. Automatic fallback to the classic path if instancing is unavailable.
- Dual simulation core — a Python reference plus an optional compiled C++ core for faster live preview, checked for numerical parity (99/99 cases) against Python.
- Adaptive viewport — crisp vector drawlist for small particle counts; above ~900 particles the particle layer renders offscreen and uploads as a texture while guides/gizmo stay vector.
- In-editor GPU preview — a minimal offscreen OpenGL 3.3 renderer built on raw
ctypes(no PyOpenGL or numpy needed). - Browser fast preview — a self-contained
live_effect.html(≈1.1 MB, zero network fetches) rendering the same effect live in Three.js (3D) / PixiJS (2D), with 500 ms live-sync. Trail mode renders ribbons in the preview too and hides the particle dots, like the editor viewport. - In-game trails (extension v0.2.0) — exported trail effects render as ribbons inside GDevelop as well (2D quad strips, 3D billboard strips, pooled and blend-aware), with
hideParticlehonored. No new objects: the existing emitters grow trail support, and trail-less effects render bit-identical to before. - OS-native file dialogs — Save / Export / Open use the OS file picker, because Dear PyGui's built-in file dialogs return empty results in this editor.
These changes belong to the viewport migration. They are in main but not in the v0.2.0 binaries: the released editor still uses its Dear PyGui viewport and the previous browser preview, and the GDevelop extension is unchanged.
- Shared browser-preview simulation (Phase 1) — the Three.js, PixiJS and Canvas adapters consume one renderer-agnostic TypeScript engine (
lempo-runtime/) and its reusable typed-array particle frames; they do not each implement their own simulation. Seeded 120-frame regression snapshots check that preview behavior is preserved. - Viewport adapters (Phase 2) — contract-based
ThreeViewportandPixiViewport, switchable throughViewportController.setMode(), with adaptive quality, persisted manual quality, and a live stats HUD. The controller owns the preview's animation-frame loop (Pixi's automatic ticker is disabled). Both paths passed a 100-cycle Chromium/WebGL lifecycle check, and Three's instanced and pooled classic fallback paths are exercised. - Python viewport bridge (Phase 3, complete) — set
LEMPO_VIEWPORT=webto launch the pywebview host from the editor; the default remainslegacy. Inspector changes are diffed into JSON Pointer patches, coalesced, and sent at up to 30 messages/s; structural changes use a full effect load. The web page consumes bridge setEffect/patch messages without pollinglast_effect.json; the file remains live for legacy preview mode. Undo/redo retains its 100-state limit and slider edits commit after mouse release. Closing the viewport window is detected and the user is offered to reopen it. - Renderer versions — the preview pins Three.js
0.185.1, matching GDevelop 5.6.284. Browser PixiJS8.21is still a different major from GDevelop's7.4.2; the compatibility difference is documented in the migration notes. - Still open — the GDevelop extension keeps its own simulation, so particle-record parity between the browser preview and the game runtime is not complete yet.
| Phase | Status |
|---|---|
| 0 · Recon, spikes & baselines | ✅ In main |
| 1 · Single browser-preview engine | ✅ In main |
| 2 · Viewport interface & renderers | ✅ In main |
| 3 · Python bridge | ✅ In main |
| 4 · Editor UX parity | ⏳ Planned |
| 5 · Parity & performance gates | ⏳ Planned |
| 6 · Deprecate legacy paths | 🔒 After Phase 5 + owner approval |
| 7 · Move editor panels to web | Optional / out of scope |
Download LempoParticleEditor.exe from the Releases page (or from itch.io) and run it. No Python installation required. The window title shows v0.2.0; it pairs with extension v0.2.0 and export format v1.2 (v1.0 and v1.1 files migrate automatically).
Requirements: Windows 10/11 (64-bit) · GPU with OpenGL 3.3 support
Run from source / rebuild
git clone https://github.com/Boy1developer/Lempo-FX.git
cd Lempo-FX
git checkout v0.2.0 # optional: the released version (main contains unreleased work)
pip install dearpygui
python editor/studio_imgui.pyTk fallback (shared logic layer, no blend/seed/field widgets — values are preserved):
python editor/particle_studio.pyRebuild the packaged app (compile check → C++ core build if stale → PyInstaller → smoke test → Windows shortcut):
python tools/rebuild_app.pyThis produces dist/LempoParticleEditor.exe plus the Lempo Particle Editor desktop shortcut (both carry the Lempo icon).
Binaries (
dist/,*.exe,*.pyd,node_modules/) are never committed. They are rebuilt locally and shipped through GitHub Releases.
- Design your effect in Lempo Particle Editor and export it as a
.jsonfile. - Import
AdvancedParticleEmitter.jsoninto your GDevelop project as an extension (requires GDevelop ≥ 5.6.284 — older versions render 3D particles at the wrong scale). - Add the emitter object to your scene and set its ParticleJSON resource to the exported file. If the effect uses a custom model, also set the Models GLB resource to your
.glb. - Run the preview and enjoy your effect in-game.
Exact action/condition names depend on the extension version — see the extension's in-editor descriptions.
Set per emitter from the sidebar dropdown. At object level, BlendingMode: 'JSON' uses each emitter's own mode; any explicit value forces all emitters.
| Mode | 2D (PixiJS) | 3D (Three.js) | Desktop GL preview | Browser preview |
|---|---|---|---|---|
| Normal | normal | normal | SRC_ALPHA, ONE_MINUS_SRC_ALPHA |
normal |
| Additive | add | additive | SRC_ALPHA, ONE |
add |
| Subtractive | erase ¹ | subtractive | reverse-subtract | erase ¹ |
| Multiply | multiply | multiply | DST_COLOR, ONE_MINUS_SRC_ALPHA |
multiply |
| Screen | screen | custom (add + ONE, ONE_MINUS_SRC_COLOR) |
ONE, ONE_MINUS_SRC_COLOR |
screen |
| Lighten | → Normal ² | custom max-equation | max | → Normal ² |
| Overlay | overlay | → Normal ³ | → Normal ³ | overlay |
¹ Pixi core has no subtract · ² Pixi core has no lighten · ³ No fixed-function overlay
Unsupported combinations fall back to Normal with a single console.warn — never per frame, never throwing. Extension versions before 0.1.2 don't know Screen / Lighten / Overlay and render them as Normal.
The shared data contract and named constants live in one place: contracts/contracts.json — particle record layout, SHAPE_ORDER, easings, blend modes, export schema, and morph window. The contract covers data and constants, not simulation code: each runtime implements its own simulation, and parity is checked by tests and tracked in the Roadmap.
Edit the JSON, run tools/generate_contracts.py, never the generated outputs. CI fails on stale files or drifted consumers.
Export format v1.2 is validated on save and load, and migrate_effect() upgrades v1.0/v1.1 files, fills missing optional values such as blend mode and seed, and normalizes camelCase easings.
👉 Full details (generated bindings, consumers, particle record layout): docs/contracts.md
🚧 = in main, not in a release yet.
| Folder | Contents |
|---|---|
editor/ |
Desktop app — Dear PyGui UI (studio_imgui.py), Tk fallback and shared logic layer (particle_studio.py) |
core/ |
C++ simulation core (particle_core.cpp) + parity and behavior tests |
render/ |
In-editor OpenGL renderer and render tests |
preview/ |
Browser fast preview (Three.js / PixiJS) and its tests. 🚧 Also: Canvas adapter, shared viewport contracts/controller/services, Three.js scene adapter with camera/gizmo/view-helper, entry points |
lempo-runtime/ |
🚧 Typed effect loader plus the renderer-agnostic TypeScript engine and trail math used by the browser preview; GDevelop retains a separate runtime implementation |
contracts/ |
Shared contracts (contracts.json) and generated bindings (gen/) |
assets/ |
App icon and bundled presets (presets/trails/) |
presets/ |
Effect presets |
bench/ |
Benchmarks |
packaging/ |
Build / packaging files for the Windows app |
tests/ |
Python test suite |
tools/ |
Build, contract-generation, and CI helper scripts |
docs/ |
Documentation and screenshots |
CI runs the parity, behavior, and contract checks on every push (.github/workflows/ci.yml). Highlights: Python ↔ C++ parity (99/99), seed replay, force fields, blend modes, trails, and the shipped GDevelop runtime tested headlessly.
🚧 main also adds 120-frame seeded preview-engine regression snapshots and typed-array frame-buffer reuse checks for the unreleased viewport work.
👉 Full test matrix: docs/testing.md
| Doc | What's inside |
|---|---|
| 🌐 Documentation site | Full visual documentation, editor guides, previews, and roadmap |
| docs/trails.md | Trails & Ribbons — modes, emit types, template format, adding your own template, C++ API |
| docs/contracts.md | Shared contracts, generated bindings, export format, particle record layout |
| docs/testing.md | Full test matrix |
| docs/viewport-migration-notes.md | 🚧 Viewport migration notes and renderer compatibility |
| APP_STRUCTURE.md | App architecture notes |
| PROGRESS.md | Development progress log |
Done
- Ready-made effect templates (Explosion, Fire, Rain, Snow)
- CI for parity and behavior tests
- Screenshots and a step-by-step GDevelop guide
- InstancedMesh batching, lazy buckets, sampling diet (v0.1.2)
- Screen / Lighten / Overlay blend modes (v0.1.2)
- Deterministic seed + force fields (v0.1.2)
- Trails & Ribbons renderer (79-key TRAIL_SCHEMA, 27 templates, Godot-style distance emission, C++ registry + DPG browser) (v0.2.0)
- Template browser with search, favorites, and user presets + world-space 2D zoom
- Over-life Bézier curves and gradient editor
Done in main — not released yet 🚧
- Shared TypeScript engine for browser-preview renderers, typed-array handoff, and seeded frame-by-frame regression checks (viewport migration Phase 1)
-
ThreeViewport/PixiViewportadapters withViewportController, adaptive quality, and a live stats HUD (viewport migration Phase 2) - Python bridge and pywebview host,
LEMPO_VIEWPORT=webintegration, coalesced editor patches, release-based undo grouping, legacy polling fallback, and viewport reopen offer (viewport migration Phase 3)
Planned
- Apply received gizmo movement to the inspector (Phase 4 UX integration; Phase 3 receives and retains
emitterMovedevents) - Match the legacy viewport's guides, gizmo, 2D zoom, trails, state colors, and shape morph in the new viewport (Phase 4)
- Golden-image tests (editor render vs browser preview) and benchmark gates (Phase 5)
- Retire the legacy viewport paths once parity is proven (Phase 6)
- Bring the GDevelop extension simulation into explicit particle-record parity with the shared browser-preview engine
- Flipbook animation and UV scroll for regular particles (trails already have them), soft particles
- Effect node tree with parent/child emitters and sub-emitters
- Timeline with scrubbing and a preset gallery
Mostafa Fathy (EG dev) — independent developer (@Boy1developer).
🎬 YouTube: youtube.com/@EG_dev
| Library | License | Used for |
|---|---|---|
| Dear PyGui | MIT | Desktop editor UI |
| Three.js | MIT | 3D rendering (browser preview + GDevelop extension) |
| PixiJS | MIT | 2D rendering (browser preview + GDevelop extension) |
Released under the MIT License.
Made by Mostafa Fathy (EG dev) · 🎬 YouTube · If this helps your game, consider giving it a ⭐












