DEV Community

PatilRB
PatilRB

Posted on

Put your particle effects in CI

Particle effects are the last asset class most web teams still treat as an opaque blob. Textures get optimized in CI, shaders get linted, JSON configs get schema-checked — and the VFX sit in a binary editor file that exactly one person on the team can open.

That asymmetry has a cost you only notice on the day someone's effect stops rendering and the git history says Modified: fire_burst.vfx (binary).

Text assets are reviewable assets

The fix is not clever, it is just a format decision: keep effects as plain JSON in the repo, next to the code that loads them. Then all the tooling you already own starts working for free.

  • git diff shows that someone raised the emission rate from 40 to 400, which is why the boss intro now costs 12 ms a frame.
  • Code review can happen at all. "Why is this effect spawning 6 sub-emitters?" is a question a reviewer can ask on a line number.
  • Merge conflicts become conflicts you can resolve instead of a coin flip between two binaries.
  • Rollback is a revert, not an archaeology project in someone's Downloads folder.

The pipeline that makes it stick

A text format only helps if there is a validation step, otherwise you have traded an unreadable file for a readable file that is also broken. The loop I settle on looks like this:

  1. Author — scaffold the effect file, then edit it in an editor or by hand.
  2. Validate — read-only check over every effect in the project, non-zero exit on error.
  3. Export — compile to a bundle the game actually loads.

Step 2 is the one that earns its keep, because it is the step you can put in CI. A validator that exits non-zero is a merge gate. The same command a developer runs before committing is the command the pipeline runs on the pull request, which means "it worked on my machine" stops being a defence.

The tool I use for this is the NixieFX CLI — three commands, no browser required:

npx nixie-fx effect create --project . --name "Fire Burst" --profile three-world-3d
npx nixie-fx validate .
npx nixie-fx export .
Enter fullscreen mode Exit fullscreen mode

validate walks every effect, touches nothing, and exits 1 if anything is wrong. That is the whole contract, and it is enough.

# .github/workflows/vfx.yml
- run: npx nixie-fx validate ./vfx
Enter fullscreen mode Exit fullscreen mode

Nine lines of pipeline config, and a broken effect can no longer reach main.

What the export step buys you

Keeping the source readable does not mean shipping the source. export writes a bundle — a manifest, compiled effect JSON, and byte copies of every referenced asset — and that bundle is what the game loads at runtime. Authoring format and shipping format are allowed to be different things, and separating them is what lets the authoring format stay verbose and diff-friendly.

It also means the deploy artifact is self-contained. No "works locally, missing texture in staging", because the bundle carries its assets rather than pointing at wherever they happened to live on the author's disk.

The part I underestimated

I expected the win to be about git hygiene. The actual win was that non-specialists could participate. When an effect is a text file with named fields and a validator that tells you when you are wrong, a gameplay programmer with no VFX background can adjust a lifetime or swap a texture and know within a second whether they broke it.

The binary format was not protecting anything. It was just the reason only one person touched the effects.


If you keep VFX in your repo, what is your validation story? I am curious whether anyone has gone further and put a perf budget in the same gate — failing the build when an effect exceeds a draw-call count would be the logical next step, and I have not built it yet.

Top comments (0)