├── CNAME ├── .gitignore ├── .hgignore ├── icon.png ├── screenshot.png ├── jsfxr-pro-screenshot.png ├── playwright.config.js ├── package.json ├── UNLICENSE ├── tests.js ├── sfxr-to-wav ├── README.md ├── logo.svg ├── riffwave.js ├── e2e-tests.js ├── index.html └── sfxr.js /CNAME: -------------------------------------------------------------------------------- 1 | sfxr.me 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | workspace 3 | node_modules 4 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | jquery-ui 3 | .DS_Store 4 | test.wav 5 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chr15m/jsfxr/HEAD/icon.png -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chr15m/jsfxr/HEAD/screenshot.png -------------------------------------------------------------------------------- /jsfxr-pro-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chr15m/jsfxr/HEAD/jsfxr-pro-screenshot.png -------------------------------------------------------------------------------- /playwright.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@playwright/test'); 2 | 3 | module.exports = defineConfig({ 4 | testDir: '.', 5 | testMatch: 'e2e-tests.js', 6 | timeout: 30000, 7 | retries: 0, 8 | use: { 9 | baseURL: 'http://localhost:8000', 10 | headless: true, 11 | viewport: { width: 1280, height: 720 }, 12 | }, 13 | webServer: { 14 | command: 'npx live-server --port=8000 --no-browser', 15 | port: 8000, 16 | reuseExistingServer: true, 17 | }, 18 | projects: [ 19 | { 20 | name: 'chromium', 21 | use: { browserName: 'chromium' }, 22 | }, 23 | ], 24 | }); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsfxr", 3 | "version": "1.3.0", 4 | "description": "8-bit sound effects generator based on sfxr", 5 | "main": "sfxr.js", 6 | "exports": [ 7 | "./sfxr.js", 8 | "./riffwave.js" 9 | ], 10 | "homepage": "https://github.com/chr15m/jsfxr", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/chr15m/jsfxr.git", 14 | "web": "https://github.com/chr15m/jsfxr" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/chr15m/jsfxr/issues/" 18 | }, 19 | "keywords": [ 20 | "sfxr", 21 | "jsfxr", 22 | "8-bit", 23 | "sfx", 24 | "sound effects", 25 | "synth", 26 | "8bit", 27 | "retro", 28 | "chiptune", 29 | "audio", 30 | "music", 31 | "sound", 32 | "game development" 33 | ], 34 | "devDependencies": { 35 | "@playwright/test": "^1.57.0", 36 | "live-server": "1.2.1", 37 | "tape": "5.6.1" 38 | }, 39 | "scripts": { 40 | "serve": "live-server", 41 | "test": "./tests.js", 42 | "test:e2e": "playwright test" 43 | }, 44 | "files": [ 45 | "sfxr.js", 46 | "riffwave.js", 47 | "UNLICENSE", 48 | "README.md", 49 | "sfxr-to-wav" 50 | ], 51 | "bin": { 52 | "sfxr-to-wav": "./sfxr-to-wav" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public 2 | domain. 3 | 4 | Anyone is free to copy, modify, publish, use, compile, sell, or 5 | distribute this software, either in source code form or as a compiled 6 | binary, for any purpose, commercial or non-commercial, and by any 7 | means. 8 | 9 | In jurisdictions that recognize copyright laws, the author or authors 10 | of this software dedicate any and all copyright interest in the 11 | software to the public domain. We make this dedication for the benefit 12 | of the public at large and to the detriment of our heirs and 13 | successors. We intend this dedication to be an overt act of 14 | relinquishment in perpetuity of all present and future rights to this 15 | software under copyright law. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | For more information, please refer to 26 | -------------------------------------------------------------------------------- /tests.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var test = require("tape"); 4 | var j = require("./sfxr"); 5 | 6 | test('test converting values from sliders to domain and back again', async function (t) { 7 | const keys = Object.keys(j.convert.sliders); 8 | for (k in j.convert.sliders) { 9 | //for (k in {"p_lpf_freq": true}) { 10 | let tolerance = 0.0001; 11 | // if (["p_arp_speed", "p_repeat_speed"].indexOf(k) != -1) continue; 12 | // special case these because the rounding in the slider conversion destroys information 13 | if (["p_arp_speed", "p_repeat_speed"].indexOf(k) != -1) tolerance = 0.004; 14 | const start = j.parameters.signed.indexOf(k) == -1 ? 0 : -1; 15 | const table = []; 16 | for (i=start; i<=1; i+=0.01) { 17 | const r1 = j.convert.sliders[k](i); 18 | const r2 = j.convert.domain[k](r1); 19 | const r3 = j.convert.domain_inverse[k](r2); 20 | const r4 = j.convert.sliders_inverse[k](r3); 21 | table[i] = {"slider": r1, "slider_inv": j.convert.sliders_inverse[k](r1), "domain": r2, "domain_inv": r3, "round_trip": r4}; 22 | //console.log(i, r1, j.convert.sliders_inverse[k](r1), r2, r3, r4); 23 | var pass = Math.abs(i - r4) < tolerance; 24 | if (!pass) { 25 | console.table(table); 26 | } 27 | t.ok(pass, k + " at " + i + " =~ " + r4); 28 | if (!pass) { 29 | process.exit(); 30 | } 31 | } 32 | console.table(table); 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /sfxr-to-wav: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | sfxr = require('./sfxr.js'); 4 | var fs = require('fs'); 5 | 6 | // defaults 7 | var outfile = "sfxr-sound.wav"; 8 | 9 | // set up initial sound parameters object 10 | var parameters = new sfxr.Params(); 11 | parameters.sound_vol = 0.25; 12 | parameters.sample_rate = 44100; 13 | parameters.sample_size = 8; 14 | 15 | // parse arguments 16 | for (a=2; a 40 | 41 | ``` 42 | 43 | You can then directly use the `sfxr` namespace without requiring it. 44 | 45 | ## API 46 | 47 | Generate a sound effect using a preset algorithm and play it using webaudio API. 48 | 49 | ```javascript 50 | const sfxr = require("jsfxr").sfxr; 51 | 52 | const preset = "pickupCoin"; 53 | const sound = sfxr.generate(preset); 54 | 55 | sfxr.play(sound); 56 | ``` 57 | 58 | Available presets are `pickupCoin`, `laserShoot`, `explosion`, `powerUp`, `hitHurt`, `jump`, `blipSelect`, `synth`, `tone`, `click`, and `random`. 59 | 60 | You can also use the interface at https://sfxr.me to find the sound you want and then use the sound definition. 61 | Click the "serialize" button and copy the JSON code for the sound you want. 62 | You will get a datastructure that you can use like this: 63 | 64 | ```javascript 65 | var sound = { 66 | "oldParams": true, 67 | "wave_type": 1, 68 | "p_env_attack": 0, 69 | "p_env_sustain": 0.31718502829007483, 70 | "p_env_punch": 0, 71 | "p_env_decay": 0.2718540993592685, 72 | "p_base_freq": 0.26126191208337196, 73 | "p_freq_limit": 0, 74 | "p_freq_ramp": 0.43787689856926615, 75 | "p_freq_dramp": 0, 76 | "p_vib_strength": 0, 77 | "p_vib_speed": 0, 78 | "p_arp_mod": 0, 79 | "p_arp_speed": 0, 80 | "p_duty": 1, 81 | "p_duty_ramp": 0, 82 | "p_repeat_speed": 0.7558565452384385, 83 | "p_pha_offset": 0, 84 | "p_pha_ramp": 0, 85 | "p_lpf_freq": 1, 86 | "p_lpf_ramp": 0, 87 | "p_lpf_resonance": 0, 88 | "p_hpf_freq": 0, 89 | "p_hpf_ramp": 0, 90 | "sound_vol": 0.25, 91 | "sample_rate": 44100, 92 | "sample_size": 8 93 | }; 94 | 95 | var a = sfxr.toAudio(sound); 96 | a.play(); 97 | ``` 98 | 99 | You can also use the short URL compressed version of the sound: 100 | 101 | ```javascript 102 | var a = sfxr.toAudio("5EoyNVSymuxD8s7HP1ixqdaCn5uVGEgwQ3kJBR7bSoApFQzm7E4zZPW2EcXm3jmNdTtTPeDuvwjY8z4exqaXz3NGBHRKBx3igYfBBMRBxDALhBSvzkF6VE2Pv"); 103 | a.play(); 104 | ``` 105 | 106 | You can also access an array of samples. 107 | By default the buffer contains audio rendered at a sample rate of `44100`. 108 | 109 | ``` 110 | var buffer = sfxr.toBuffer(sound); 111 | console.log(buffer); 112 | ``` 113 | 114 | Additionally you can get a dataURI for a wav file of the sound: 115 | 116 | ``` 117 | var a = sfxr.toWave(sound); 118 | console.log(a.dataURI); 119 | ``` 120 | 121 | You can convert between the base58 short-url encoded format and JSON using `b58encode` and `b58decode`: 122 | 123 | ``` 124 | var b58string = sfxr.b58encode(sound); 125 | var sound = sfxr.b58decode(b58string); 126 | ``` 127 | 128 | You can also access the lower level classes `SoundEffect` and `Params` if you need to. 129 | This can be useful for caching the internal representation for efficiency, or mutating the sound with `params.mutate()`. 130 | 131 | # Jsfxr Pro 132 | 133 | A [Pro version of Jsfxr](https://pro.sfxr.me/) is available with enhanced features like the ability to save sounds to packs and download a zip file of all wavs. 134 | 135 | [![Jsfxr Pro Screenshot](./jsfxr-pro-screenshot.png)](https://pro.sfxr.me) 136 | 137 | # Links 138 | 139 | * Application: http://sfxr.me/ 140 | * Source code: https://github.com/chr15m/jsfxr/ 141 | 142 | # Thanks 143 | 144 | * Dr. Petter for inventing sfxr. 145 | * Eric Fredricksen for creating this port. 146 | * riffwave.js: http://www.codebase.es/riffwave/ 147 | * jquery-ui: http://jqueryui.com/ 148 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /riffwave.js: -------------------------------------------------------------------------------- 1 | /* 2 | * RIFFWAVE.js v0.03 - Audio encoder for HTML5