{"id":40951603,"url":"https://github.com/jet2jet/js-synthesizer","last_synced_at":"2026-01-22T05:13:35.890Z","repository":{"id":45295697,"uuid":"155714552","full_name":"jet2jet/js-synthesizer","owner":"jet2jet","description":"Synthesizer library for JS program, using with Web Audio or etc.","archived":false,"fork":false,"pushed_at":"2025-11-03T00:33:39.000Z","size":3151,"stargazers_count":65,"open_issues_count":7,"forks_count":8,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-11-03T02:26:55.445Z","etag":null,"topics":["javascript","midi","midi-player","nodejs","soundfont","synth","webaudio"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jet2jet.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-11-01T12:52:44.000Z","updated_at":"2025-11-03T00:33:42.000Z","dependencies_parsed_at":"2024-04-19T09:28:48.781Z","dependency_job_id":"adc3fdd5-5799-4fec-ab8f-abb297df3b65","html_url":"https://github.com/jet2jet/js-synthesizer","commit_stats":{"total_commits":103,"total_committers":4,"mean_commits":25.75,"dds":0.07766990291262132,"last_synced_commit":"4468c01e70d8665d76cfe11e37770bb3351ce348"},"previous_names":["jet2jet/fluid-js"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/jet2jet/js-synthesizer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jet2jet%2Fjs-synthesizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jet2jet%2Fjs-synthesizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jet2jet%2Fjs-synthesizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jet2jet%2Fjs-synthesizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jet2jet","download_url":"https://codeload.github.com/jet2jet/js-synthesizer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jet2jet%2Fjs-synthesizer/sbom","scorecard":{"id":516909,"data":{"date":"2025-08-11","repo":{"name":"github.com/jet2jet/js-synthesizer","commit":"bfd1e332ac649e4d5ba9ab8b0a104af8b7981250"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.1,"checks":[{"name":"Maintained","score":8,"reason":"9 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 8","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 1/27 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/publish.yml:15","Warn: no topLevel permission defined: .github/workflows/nodevars.yml:1","Warn: no topLevel permission defined: .github/workflows/publish.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/jet2jet/js-synthesizer/publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/jet2jet/js-synthesizer/publish.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/publish.yml:27","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yml:11"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T02:04:25.319Z","repository_id":45295697,"created_at":"2025-08-20T02:04:25.319Z","updated_at":"2025-08-20T02:04:25.319Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28655390,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","midi","midi-player","nodejs","soundfont","synth","webaudio"],"created_at":"2026-01-22T05:13:35.111Z","updated_at":"2026-01-22T05:13:35.885Z","avatar_url":"https://github.com/jet2jet.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM version](https://badge.fury.io/js/js-synthesizer.svg)](https://www.npmjs.com/package/js-synthesizer)\n\n# js-synthesizer\n\njs-synthesizer is a library that generates audio data (frames). [WebAssembly (wasm) version of FluidSynth](https://github.com/jet2jet/fluidsynth-emscripten) is used as a core synthesizer engine.\n\n## Demo\n\nhttps://www.pg-fl.jp/music/js-synthesizer/index.en.htm\n\n## Install\n\n```\nnpm install --save js-synthesizer\n```\n\n## Usage\n\n### From main thread\n\nCopies `dist/js-synthesizer.js` (or `dist/js-synthesizer.min.js`) and `externals/libfluidsynth-2.4.6.js` (libfluidsynth JS file) to your project, and writes `\u003cscript\u003e` tags as following order:\n\n```html\n\u003cscript src=\"libfluidsynth-2.4.6.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"js-synthesizer.js\"\u003e\u003c/script\u003e\n```\n\n\u003e If you want to use Soundfont version 3 (.sf3) files, use `externals/libfluidsynth-2.4.6-with-libsndfile.js` instead of `externals/libfluidsynth-2.4.6.js`.\n\nWhen scripts are available, please check whether `waitForReady` resolves.\n\n```js\nJSSynth.waitForReady().then(loadSynthesizer);\n\nfunction loadSynthesizer() {\n    // process with JSSynth...\n}\n```\n\nAfter initialized, you can use APIs via `JSSynth` namespace object.\n\n```js\n// Prepare the AudioContext instance\nvar context = new AudioContext();\nvar synth = new JSSynth.Synthesizer();\nsynth.init(context.sampleRate);\n\n// Create AudioNode (ScriptProcessorNode) to output audio data\nvar node = synth.createAudioNode(context, 8192); // 8192 is the frame count of buffer\nnode.connect(context.destination);\n\n// Load your SoundFont data (sfontBuffer: ArrayBuffer)\nsynth.loadSFont(sfontBuffer).then(function () {\n    // Load your SMF file data (smfBuffer: ArrayBuffer)\n    return synth.addSMFDataToPlayer(smfBuffer);\n}).then(function () {\n    // Play the loaded SMF data\n    return synth.playPlayer();\n}).then(function () {\n    // Wait for finishing playing\n    return synth.waitForPlayerStopped();\n}).then(function () {\n    // Wait for all voices stopped\n    return synth.waitForVoicesStopped();\n}).then(function () {\n    // Releases the synthesizer\n    synth.close();\n}, function (err) {\n    console.log('Failed:', err);\n    // Releases the synthesizer\n    synth.close();\n});\n```\n\n(Above example uses Web Audio API, but you can use `Synthesizer` without Web Audio, by using `render()` method.)\n\njs-synthesizer is built as UMD module. If you prefer to load js-synthesizer as a CommonJS / ES module, you can use `import` statement such as `import * as JSSynth from 'js-synthesizer'` by using bundlers (such as webpack).\n\nNotes:\n\n* `js-synthesizer.js` intends the ES2015-supported environment. If you need to run the script without errors on non-ES2015 environment such as IE11 (to notify 'unsupported'), you should load those scripts dynamically, or use transpiler such as babel.\n* When just after the scripts loaded, some APIs may fail since libfluidsynth is not ready. To avoid this, you can use the Promise object returned by `JSSynth.waitForReady` as above example.\n* libfluidsynth JS file is not `import`-able (but `require`-able from 2.4.6) and its license (LGPL v2.1) is different from js-synthesizer's (BSD-3-Clause).\n\n### With AudioWorklet\n\njs-synthesizer supports AudioWorklet process via `dist/js-synthesizer.worklet.js` (or `dist/js-synthesizer.worklet.min.js`). You can load js-synthesizer on the AudioWorklet as the following code:\n\n```js\nvar context = new AudioContext();\ncontext.audioWorklet.addModule('libfluidsynth-2.4.6.js')\n    .then(function () {\n        return context.audioWorklet.addModule('js-synthesizer.worklet.js');\n    })\n    .then(function () {\n        // Create the synthesizer instance for AudioWorkletNode\n        var synth = new JSSynth.AudioWorkletNodeSynthesizer();\n        synth.init(context.sampleRate);\n        // You must create AudioWorkletNode before using other methods\n        // (This is because the message port is not available until the\n        // AudioWorkletNode is created)\n        audioNode = synth.createAudioNode(context);\n        audioNode.connect(context.destination); // or another node...\n        // After node creation, you can use Synthesizer methods\n        return synth.loadSFont(sfontBuffer).then(function () {\n            return synth.addSMFDataToPlayer(smfBuffer);\n        }).then(function () {\n            return synth.playPlayer();\n        }).then(function () {\n            ...\n        });\n    });\n```\n\n### With Web Worker\n\njs-synthesizer and libfluidsynth can be executed on a Web Worker. Executing on a Web Worker prevents from blocking main thread while rendering.\n\nTo use js-synthesizer on a Web Worker, simply call `importScripts` as followings:\n\n```js\nself.importScripts('libfluidsynth-2.4.6.js');\nself.importScripts('js-synthesizer.js');\n```\n\nNote that since the Web Audio is not supported on the Web Worker, the APIs/methods related to the Web Audio will not work. If you want to use both Web Worker and AudioWorklet, you should implement AudioWorkletProcessor manually as followings:\n\n* main thread -- create AudioWorkletNode and establish connections between Web Worker and AudioWorklet\n    * You need to transfer rendered audio frames from Web Worker to AudioWorklet, but AudioWorklet environment does not support creating Web Worker. By creating `MessageChannel` and sending its port instances to Web Worker and AudioWorklet, they can communicate each other directly.\n* Web Worker thread -- render audio frames into raw buffers and send it for AudioWorklet thread\n* AudioWorklet thread -- receive audio frames (and queue) and 'render' it in the `process` method\n\n### In Node.js environment\n\nFrom fluidsynth-emscripten version 2.4.6, the library supports Node.js environment. js-synthesizer also supports for this since 1.11.0.\n\nTo use js-synthesizer in Node.js environment, load with `require` function:\n\n```js\nconst libfluidsynth = require('js-synthesizer/libfluidsynth');\nconst JSSynth = require('js-synthesizer');\n\n// Initialize with loaded libfluidsynth\nJSSynth.Synthesizer.initializeWithFluidSynthModule(libfluidsynth);\n\nJSSynth.waitForReady().then(() =\u003e {\n  // Now JSSynth.Synthesizer can be constructed\n});\n```\n\n## API\n\n### Creation of Synthesizer instance\n\nThese classes implement the interface named `JSSynth.ISynthesizer`.\n\n* `JSSynth.Synthesizer` (construct: `new JSSynth.Synthesizer()`)\n    * Creates the general synthesizer instance. No parameters are available.\n* `JSSynth.AudioWorkletNodeSynthesizer` (construct: `new JSSynth.AudioWorkletNodeSynthesizer()`)\n    * Creates the synthesizer instance communicating AudioWorklet (see above). No parameters are available.\n    * You must call `createAudioNode` method first to use other instance methods.\n\n### Creation of Sequencer instance\n\nThe `Sequencer` instance is created only via following methods:\n\n* `JSSynth.Synthesizer.createSequencer` (static method)\n    * Returns the Promise object that resolves with `JSSynth.ISequencer` instance. The instance can be used with `JSSynth.Synthesizer` instances.\n* `JSSynth.AudioWorkletNodeSynthesizer.prototype.createSequencer` (instance method)\n    * Returns the Promise object that resolves with `JSSynth.ISequencer` instance. The instance can be used with `JSSynth.AudioWorkletNodeSynthesizer` instances which handled `createSequencer` calls.\n\n### Using hook / handle MIDI-related event data with user-defined callback\n\nYou can hook MIDI events posted by player. For `JSSynth.Synthesizer` instance, use `hookPlayerMIDIEvents` method as followings:\n\n```js\nsyn.hookPlayerMIDIEvents(function (s, type, event) {\n    // hook '0xC0' event (Program Change event)\n    if (type === 0xC0) {\n        // if the 'program' value is 0, use another SoundFont\n        if (event.getProgram() === 0) {\n            syn.midiProgramSelect(event.getChannel(), secondSFont, 0, 0);\n            return true;\n        }\n    }\n    // return false to use default processings for other events\n    return false;\n});\n```\n\nFor `JSSynth.AudioWorkletNodeSynthesizer` instance, use `hookPlayerMIDIEventsByName` as followings:\n\n* worklet.js\n\n```js\n// We must add method to AudioWorkletGlobalScope to pass to another module.\nAudioWorkletGlobalScope.myHookPlayerEvents = function (s, type, event, data) {\n    if (type === 0xC0) {\n        if (event.getProgram() === 0) {\n            // 'secondSFont' will be passed from 'hookPlayerMIDIEventsByName'\n            s.midiProgramSelect(event.getChannel(), data.secondSFont, 0, 0);\n            return true;\n        }\n    }\n    return false;\n};\n```\n\n* main.js\n\n```js\n// before use this, 'worklet.js' above must be loaded as AudioWorklet completely, and\n// syn.createAudioNode must be called to activate worklet.\n\n// The first parameter is the method name added to 'AudioWorkletGlobalScope'.\n// The second parameter will be passed to the worklet.\nsyn.hookPlayerMIDIEventsByName('myHookPlayerEvents', { secondSFont: secondSFont });\n```\n\nThe sequencer also supports 'user-defined client' to handle event data.\n\n* For sequncer instance created by `Synthesizer.createSequencer`, use `Synthesizer.registerSequencerClient` static method.\n    * You can use `Synthesizer.sendEventNow` static method to send event data, processed by the synthesizer or clients, to another clients/synthesizers.\n* For sequncer instance created by `createSequencer` of `AudioWorkletNodeSynthesizer`, use `registerSequencerClientByName` instance method.\n    * The callback function must be added to 'AudioWorkletGlobalScope' like `hookPlayerMIDIEventsByName`'s callback.\n    * To re-send event data, use `Synthesizer.sendEventNow` in the worklet. `Synthesizer` constructor is available via `AudioWorkletGlobalScope.JSSynth.Synthesizer`.\n* You can rewrite event data passed to the callback, by using `JSSynth.rewriteEventData` (`AudioWorkletGlobalScope.JSSynth.rewriteEventData` for worklet).\n\n### `JSSynth` methods\n\n#### `waitForReady`\n\nCan be used to wait for the synthesizer engine's ready.\n\nReturn: `Promise` object (resolves when the synthesizer engine (libfluidsynth) is ready)\n\n#### `JSSynth.Synthesizer.initializeWithFluidSynthModule`\n\nInitializes js-synthesizer with (explicitly) loaded libfluidsynth library.\n\n#### `disableLogging` / `restoreLogging`\n\nCan be used to suppress logs from libfluidsynth.\n\n### `JSSynth.ISynthesizer` methods\n\n(Not documented yet. Please see `dist/lib/ISynthesizer.d.ts`.)\n\n## License\n\njs-synthesizer is licensed under [BSD 3-Clause License](./LICENSE) except for the files in `externals` directory.\nFor licenses of the files in `externals` directory, please read [`externals/README.md`](./externals/README.md).\n\n\u003e `libfluidsynth` directory is only a wrapper for `externals/libfluidsynth-*.js`, but uses same license of libfluidsynth for convenience.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjet2jet%2Fjs-synthesizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjet2jet%2Fjs-synthesizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjet2jet%2Fjs-synthesizer/lists"}