ZzFX generates sound effects from a single array of numbers
A Tiny JavaScript Sound FX System
At a glance
- What is it?
- ZzFX is a tiny JavaScript sound synthesizer that replaces audio asset files with generated buffers, aimed at size-conscious web and game projects. It works well for one-line effects, and it stops being the right tool the moment you need music or a designed sound.
- Who is it for?
- Adopt ZzFX for prototypes, game jams, size-limited web builds and projects that need placeholder effects generated from code rather than shipped as files. Skip it when the sound needs musical structure or a designed timbre, since ZzFXM is a separate project and ZzFX itself offers only parameterised synthesis.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Sound effects without shipping audio files
ZzFX answers a narrow question: how do you add sound to a browser project without adding megabytes of audio assets. The library is described as a tiny sound generator designed to produce a wide variety of sound effects with minimal code overhead, and the README lists games, prototypes and any web application that needs sound as the intended users.
The size argument is the whole premise. The feature list states that ZzFX stays under one kilobyte when compressed, ships with no external libraries, and runs in nearly all web browsers. Audio is synthesized at runtime by the browser, so a project pays nothing in download size for the effects it uses. The README frames this for size-limited competitions like JS13K, and the games list in the project documentation is largely made up of JS13K entries.
There is a second reason to care, which is speed of iteration. Adding or changing a sound effect is one line of code rather than an asset edit and an export step, and the same line can be pasted anywhere. For a prototype where the exact timbre does not matter yet, that is the difference between an effect that exists and one that waits for the audio pipeline.
The cost is fixed. You are choosing from what a synthesizer can generate, not from what an artist recorded.
Twenty parameters packed into one positional array
The calling convention is unusual and worth understanding before you write anything. A sound is described by a flat array of numbers, and unused slots are left empty, which is why the README's examples are full of commas:
zzfx(...[,,925,.04,.3,.6,1,.3,,6.27,-184,.09,.17]); // Game Over
zzfx(...[,,537,.02,.02,.22,1,1.59,-6.98,4.97]); // Heart
zzfx(...[1.5,.8,270,,.1,,1,1.5,,,,,,,,.1,.01]); // Piano
zzfx(...[,,129,.01,,.15,,,,,,,,5]); // DrumThe spread operator passes the array as positional arguments, and each index means one specific property of the sound. The feature list counts twenty controllable parameters, and the micro build in the README shows their names in the function signature: volume and the audio context live outside, while the call itself takes parameters up to the twenty-first slot. Empty slots mean the default is kept, which is why the drum example has a long run of empty commas before its final value.
The upside is compactness and shareability. The sound designer app supports loading sounds via pasted zzfx code, so a single line can be handed to someone else as the entire sound.
The downside is that nothing is self-describing. A long array of bare numbers with no names attached is not something a reader can understand without the parameter list, and any reordering of the array silently changes the sound rather than raising an error.
ZzFXMicro is the whole engine in one file
The repository ships three related files: ZzFX.js, ZzFXMicro.js and ZzFXMicro.min.js, plus a wav.js helper and an index.html for the site. The micro variant is the one worth understanding, because it is the entire engine in a single expression.
Reading the micro source, the engine creates an AudioContext, fills a buffer at a 44100 sample rate, writes the generated waveform into the channel data, and starts a buffer source connected to the context destination. Sound is therefore rendered into a buffer at the moment it is requested, not scheduled note by note through the audio graph. That design is what makes the compressed size possible, and it is why the README lists precaching generated sounds and playing them later instantly as a feature: rendering happens once, playback is a plain buffer start.
The generator itself is a phase accumulator. Waveform selection is switched on the value of a parameter, with distinct branches for square, saw, triangle, sine and noise style output, and a further branch produces a cube-shaped wave. The same code path handles sample-and-hold behaviour and, when the loop parameter is set, repeats the generated cycle for the requested number of samples. Pitch and frequency are expressed in a compact internal unit and converted against the sample rate, which is why the same array produces different pitches at different playback rates.
A release note in the project history shows the direction of travel: version 1.1.8 separated the play and generate functions in zzfxm, so a sound can be generated as data and played separately.
Getting ZzFX into a project and making the first sound
There are three documented ways in, and they suit different situations. The package is published to npm:
npm install zzfxThe package manifest names ZzFX.js as the main entry point and ships four files: ZzFX.js, ZzFXMicro.js, ZzFXMicro.min.js and wav.js. It declares an MIT licence and its test script is simply a zzfx() call, which tells you how small the surface really is.
For a project without a build step, the README suggests pasting the code from ZzFXMicro.min.js directly into the page. The import form is a named import from the local file:
import {ZZFX, zzfx} from './ZzFX.js'Playing a sound is then a single call, and the README's shortest example passes a sparse array that sets only the volume:
zzfx(...[,,,,.1,,,,9])You should hear a short effect immediately on a user gesture, since the audio context is created when the module loads. If nothing is heard, the likely cause is browser autoplay policy rather than a ZzFX fault, and the README does not document a resume call for that case.
A second option is to build the sound first in the sound designer web app and paste the resulting code back into your project. That workflow keeps the parameter array and the creative iteration in the same place.
The sound designer app is the real workflow tool
Beyond the library, the project maintains a web app for crafting sounds, and it is where the twenty parameters stop being abstract. The README describes random sound generation from presets, sound management with auto-save, rename and favourite, and a wave image visualization shown on playback.
Two features matter for a working setup. Sharing works by loading sounds from pasted zzfx code, so a sound is portable as text. Export and import cover sound lists with drag and drop, and offline playback saves sounds as .wav files, which is the escape hatch for when a generated effect needs to end up in an asset pipeline instead of being synthesized at runtime.
The app also plays a part in music, indirectly. ZzFX now supports music via ZzFXM by Keith Clark, a separate project with its own tiny player and songs that is compatible with NoteCraft exports. ZzFXM sits outside this repository, and the README describes it as a super tiny player and songs. If you need a tune rather than an effect, that is the branch of the project to look at, and it is a dependency decision rather than a feature of ZzFX itself.
For reference, the soundboard demo linked from the project page provides a quick way to audition the shipped sample sounds in a browser.
Where the one kilobyte budget forces trade-offs
The constraint is the feature set. Because everything must fit in a compressed kilobyte and depend on nothing, the engine offers a fixed set of waveforms and a fixed set of twenty parameters. There is no reverb, no bus processing, no sample layering and no per-sound mixing, because each of those would need code that the size budget cannot hold.
The second limit is musical. ZzFX is an effect generator. Sustained melodies, chord progressions and anything with arrangement need the separate ZzFXM work, and the README is explicit that music support comes from that external project. The word 'tiny' in the feature list refers to the whole library, not to a subset.
A third limit is portability of assets. Generated sounds live in memory and in code. If a designer later wants to change an effect in a DAW, adjust it, or reuse it across projects, the workflow has to be reversed: export a .wav from the designer app and treat the result as a normal audio file. The project supports that path, but it means the code-first approach is a generation technique rather than a replacement for a sound pipeline.
The Playdate port shows the same idea applied to a different host: ZzFX is available for the Playdate portable console through a separate repository.
ZzFX against shipping pre-rendered audio files
The realistic alternative is the ordinary route: export a .wav or .mp3 from an audio tool, place it in the project, and load it through the Web Audio API or an HTML audio element.
The difference in approach is where the sound comes from. A recorded asset can be any timbre the artist imagined, layered, spatialised and edited to taste; it also costs download size and requires an export and import step for every change. A generated sound is math, produced at runtime, with the cost compressed into code and the creative limit set by the parameters.
The size argument only bites under a budget. In a normal web application with a megabyte budget, the difference between one compressed kilobyte of ZzFX and a folder of short effects is not the deciding factor, and the loss of artistic control is a real cost. In a 13 kilobyte game, or a jam entry, or a prototype where the effect only has to read as 'hit' or 'jump', the exchange is clearly worth it.
There is a middle path worth naming. ZzFX's own designer app can export .wav files, so the library can be used to generate placeholder effects quickly and then hand the exported files to a designer for final versions. That keeps the speed of code-driven iteration without freezing the finished sound in the generator's limits.
MIT licence, frequent pushes, infrequent tagged releases
ZzFX is MIT licensed, with the licence text in the LICENSE file at the repository root and the same identifier declared in the package manifest. The author field names Frank Force. The MIT terms are permissive enough to drop the library into a commercial or closed-source project without licensing friction, though the licence covers the code only, not any sounds you export from the designer app.
The maintenance picture is a little uneven. The repository is not archived and the last push was on 2026-10-01, so changes do land. The tagged releases are older: version 1.3.0 on 2024-07-01, which added a filter parameter with low or high pass options, version 1.1.8 on 2021-08-19 and version 1.1.7 on 2021-08-12. The package manifest already reads 1.4.0, and the micro source header also names version 1.4.0, which suggests releases are cut less often than commits.
For adopters the upgrade path is trivial in one direction and awkward in the other. Adding or upgrading the library is a matter of replacing a single file, and the README notes there are no dependencies to reconcile. But the positional parameter arrays in existing code are tied to that version, so a release that reorders or inserts a parameter slot changes the meaning of every sound array in the codebase. Check the parameter order in ZzFX.js before bumping a version, and keep your own copy of the array comments.
Editorial conclusion
Adopt ZzFX for prototypes, game jams, size-limited web builds and projects that need placeholder effects generated from code rather than shipped as files. Skip it when the sound needs musical structure or a designed timbre, since ZzFXM is a separate project and ZzFX itself offers only parameterised synthesis. Verify first that the target environment exposes the Web Audio API, and read the parameter list in ZzFX.js before copying the positional array syntax, because the order of the twenty slots is what makes or breaks a sound. The last push was on 2026-10-01.
Frequently asked questions
How do I install and use ZzFX in a JavaScript project?
Run npm install zzfx, then import the module with import {ZZFX, zzfx} from './ZzFX.js', or paste the code from ZzFXMicro.min.js if you have no build step. Playing a sound is a call such as zzfx(...[,,,,.1,,,,9]).
What do the numbers in a ZzFX parameter array mean?
Each position in the array is one of the twenty controllable parameters, and empty slots keep the default value. The micro build in the README names the parameters in the function signature, so that list is the reference for the order.
Can ZzFX play music as well as sound effects?
Music support comes from ZzFXM by Keith Clark, a separate project with a tiny player and songs that is compatible with NoteCraft exports. ZzFX itself is the effect generator.
How do I export a ZzFX sound to a normal audio file?
The ZzFX sound designer web app can save sounds as .wav files for offline playback, and it also supports export and import of sound lists by drag and drop. A release note for version 1.1.8 added separate play and generate functions in zzfxm, so a sound can be produced as data before it is played.
What is the licence and the latest release of ZzFX?
The library is MIT licensed, declared both in the package manifest and in the LICENSE file at the repository root. The most recent tagged release is v1.3.0 from 2024-07-01, while the package manifest reads 1.4.0 and the last push was on 2026-10-01.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/killedbyapixel-zzfx)Was this useful?