{"id":14007020,"url":"https://github.com/danigb/smplr","last_synced_at":"2025-04-07T11:08:24.870Z","repository":{"id":57364341,"uuid":"47576360","full_name":"danigb/smplr","owner":"danigb","description":"A web audio sampler instrument","archived":false,"fork":false,"pushed_at":"2024-04-11T16:50:46.000Z","size":4132,"stargazers_count":131,"open_issues_count":0,"forks_count":12,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-04-14T07:58:44.850Z","etag":null,"topics":["audio-sampler","instruments","web-audio"],"latest_commit_sha":null,"homepage":"https://danigb.github.io/smplr/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danigb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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}},"created_at":"2015-12-07T20:08:36.000Z","updated_at":"2024-04-06T00:39:11.000Z","dependencies_parsed_at":"2022-09-13T21:11:09.819Z","dependency_job_id":"c126e56d-517c-4e77-8b1c-ffdac2314ad8","html_url":"https://github.com/danigb/smplr","commit_stats":{"total_commits":18,"total_committers":1,"mean_commits":18.0,"dds":0.0,"last_synced_commit":"32ce433f019a95bc9bd8f1b670e9984cb9d885bf"},"previous_names":["danigb/samplr"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danigb%2Fsmplr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danigb%2Fsmplr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danigb%2Fsmplr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danigb%2Fsmplr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danigb","download_url":"https://codeload.github.com/danigb/smplr/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247640464,"owners_count":20971557,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["audio-sampler","instruments","web-audio"],"created_at":"2024-08-10T10:01:46.425Z","updated_at":"2025-04-07T11:08:24.854Z","avatar_url":"https://github.com/danigb.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# [smplr](https://github.com/danigb/smplr)\n\n[![npm version](https://img.shields.io/npm/v/smplr)](https://www.npmjs.com/package/smplr)\n\n\u003e `smplr` is a collection of sampled instruments for Web Audio API ready to be used with no setup required.\n\nExamples:\n\n```js\nimport { Soundfont } from \"smplr\";\n\nconst context = new AudioContext();\nconst marimba = new Soundfont(context, { instrument: \"marimba\" });\nmarimba.start({ note: 60, velocity: 80 });\n```\n\n```js\nimport { DrumMachine } from \"smplr\";\n\nconst context = new AudioContext();\nconst dm = new DrumMachine(context);\ndm.start({ note: \"kick\" });\n```\n\n```js\nimport { SplendidGrandPiano, Reverb } from \"smplr\";\n\nconst context = new AudioContext();\nconst piano = new SplendidGrandPiano(context);\npiano.output.addEffect(\"reverb\", new Reverb(context), 0.2);\n\npiano.start({ note: \"C4\" });\n```\n\nSee demo: https://danigb.github.io/smplr/\n\n`smplr` is still under development and features are considered unstable until v 1.0\n\nRead [CHANGELOG](https://github.com/danigb/smplr/blob/main/CHANGELOG.md) for changes.\n\n#### Library goals\n\n- No setup: specifically, all samples are online, so no need for a server.\n- Easy to use: everything should be intuitive for non-experienced developers\n- Decent sounding: uses high quality open source samples. For better or worse, it is sample based 🤷\n\n## Setup\n\nYou can install the library with a package manager or use it directly by importing from the browser.\n\nSamples are stored at https://github.com/smpldsnds and there is no need to download them. Kudos to all _samplerist_ 🙌\n\n#### Using a package manger\n\nUse npm or your favourite package manager to install the library to use it in your project:\n\n```\nnpm i smplr\n```\n\n#### Usage from the browser\n\nYou can import directly from the browser. For example:\n\n```html\n\u003chtml\u003e\n  \u003cbody\u003e\n    \u003cbutton id=\"btn\"\u003eplay\u003c/button\u003e\n  \u003c/body\u003e\n  \u003cscript type=\"module\"\u003e\n    import { SplendidGrandPiano } from \"https://unpkg.com/smplr/dist/index.mjs\"; // needs to be a url\n    const context = new AudioContext(); // create the audio context\n    const marimba = new SplendidGrandPiano(context); // create and load the instrument\n\n    document.getElementById(\"btn\").onclick = () =\u003e {\n      context.resume(); // enable audio context after a user interaction\n      marimba.start({ note: 60, velocity: 80 }); // play the note\n    };\n  \u003c/script\u003e\n\u003c/html\u003e\n```\n\nThe package needs to be serve as a url from a service like [unpkg](unpkg.com) or similar.\n\n## Documentation\n\n### Create and load an instrument\n\nAll instruments follows the same pattern: `new Instrument(context, options)`. For example:\n\n```js\nimport { SplendidGrandPiano, Soundfont } from \"smplr\";\n\nconst context = new AudioContext();\nconst piano = new SplendidGrandPiano(context, { decayTime: 0.5 });\nconst marimba = new Soundfont(context, { instrument: \"marimba\" });\n```\n\n#### Wait for audio loading\n\nYou can start playing notes as soon as one audio is loaded. But if you want to wait for all of them, you can use the `load` property that returns a promise:\n\n```js\npiano.load.then(() =\u003e {\n  // now the piano is fully loaded\n});\n```\n\nSince the promise returns the instrument instance, you can create and wait in a single line:\n\n```js\nconst piano = await new SplendidGrandPiano(context).load;\n```\n\n⚠️ In versions lower than 0.8.0 a `loaded()` function was exposed instead.\n\n#### Shared configuration options\n\nAll instruments share some configuration options that are passed as second argument of the constructor. As it name implies, all fields are optional:\n\n- `volume`: A number from 0 to 127 representing the instrument global volume. 100 by default\n- `destination`: An `AudioNode` that is the output of the instrument. `AudioContext.destination` is used by default\n- `volumeToGain`: a function to convert the volume to gain. It uses MIDI standard as default.\n- `disableScheduler`: disable internal scheduler. `false` by default.\n- `scheduleLookaheadMs`: the lookahead of the scheduler. If the start time of the note is less than current time plus this lookahead time, the note will be started. 200ms by default.\n- `scheduleIntervalMs`: the interval of the scheduler. 50ms by default.\n- `onStart`: a function that is called when starting a note. It receives the note started as parameter. Bear in mind that the time this function is called is not precise, and it's determined by lookahead.\n- `onEnded`: a function that is called when the note ends. It receives the started note as parameter.\n\n#### Usage with standardized-audio-context\n\nThis package should be compatible with [standardized-audio-context](https://github.com/chrisguttandin/standardized-audio-context):\n\n```js\nimport { AudioContext } from \"standardized-audio-context\";\n\nconst context = new AudioContext();\nconst piano = new SplendidGrandPiano(context);\n```\n\nHowever, if you are using Typescript, you might need to \"force cast\" the types:\n\n```ts\nimport { Soundfont } from \"smplr\";\nimport { AudioContext as StandardizedAudioContext } from \"standardized-audio-context\";\n\nconst context = new StandardizedAudioContext() as unknown as AudioContext;\nconst marimba = new Soundfont(context, { instrument: \"marimba\" });\n```\n\n### Play\n\n#### Start and stop notes\n\nThe `start` function accepts a bunch of options:\n\n```js\npiano.start({ note: \"C4\", velocity: 80, time: 5, duration: 1 });\n```\n\nThe `velocity` is a number between 0 and 127 the represents at which velocity the key is pressed. The bigger the number, louder the sound. But `velocity` not only controls the loudness. In some instruments, it also affects the timbre.\n\nThe `start` function returns a `stop` function for the given note:\n\n```js\nconst stopNote = piano.start({ note: 60 });\nstopNote({ time: 10 });\n```\n\nBear in mind that you may need to call [`context.resume()` before playing a note](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Best_practices#autoplay_policy)\n\nInstruments have a global `stop` function that can be used to stop all notes:\n\n```js\n// This will stop all notes\npiano.stop();\n```\n\nOr stop the specified one:\n\n```js\n// This will stop C4 note\npiano.stop(60);\n```\n\n#### Schedule notes\n\nYou can schedule notes using `time` and `duration` properties. Both are measured in seconds. Time is the number of seconds since the AudioContext was created, like in `audioContext.currentTime`\n\nFor example, next example plays a C major arpeggio, one note per second:\n\n```js\nconst now = context.currentTime;\n[\"C4\", \"E4\", \"G4\", \"C5\"].forEach((note, i) =\u003e {\n  piano.start({ note, time: now + i, duration: 0.5 });\n});\n```\n\n#### Looping\n\nYou can loop a note by using `loop`, `loopStart` and `loopEnd`:\n\n```js\nconst sampler = new Sampler(audioContext, { duh: \"duh-duh-ah.mp3\" });\nsampler.start({\n  note: \"duh\"\n  loop: true\n  loopStart: 1.0,\n  loopEnd: 9.0,\n});\n```\n\nIf `loop` is true but `loopStart` or `loopEnd` are not specified, 0 and total duration will be used by default, respectively.\n\n#### Change volume\n\nInstrument `output` attribute represents the main output of the instrument. `output.setVolume` method accepts a number where 0 means no volume, and 127 is max volume without amplification:\n\n```js\npiano.output.setVolume(80);\n```\n\n⚠️ `volume` is global to the instrument, but `velocity` is specific for each note.\n\n#### Events\n\nTwo events are supported `onStart` and `onEnded`. Both callbacks will receive as parameter started note.\n\nEvents can be configured globally:\n\n```js\nconst context = new AudioContext();\nconst sampler = new Sample(context, {\n  onStart: (note) =\u003e {\n    console.log(note.time, context.currentTime);\n  },\n});\n```\n\nor per note basis:\n\n```js\npiano.start({\n  note: \"C4\",\n  duration: 1,\n  onEnded: () =\u003e {\n    // will be called after 1 second\n  },\n});\n```\n\nGlobal callbacks will be invoked regardless of whether local events are defined.\n\n⚠️ The invocation time of `onStart` is not exact. It triggers slightly before the actual start time and is influenced by the `scheduleLookaheadMs` parameter.\n\n### Effects\n\n#### Reverb\n\nAn packed version of [DattorroReverbNode](https://github.com/khoin/DattorroReverbNode) algorithmic reverb is included.\n\nUse `output.addEffect(name, effect, mix)` to connect an effect using a send bus:\n\n```js\nimport { Reverb, SplendidGrandPiano } from \"smplr\";\nconst reverb = new Reverb(context);\nconst piano = new SplendidGrandPiano(context, { volume });\npiano.output.addEffect(\"reverb\", reverb, 0.2);\n```\n\nTo change the mix level, use `output.sendEffect(name, mix)`:\n\n```js\npiano.output.sendEffect(\"reverb\", 0.5);\n```\n\n### Experimental features\n\n#### Cache requests\n\nIf you use default samples, they are stored at github pages. Github rate limits the number of requests per second. That could be a problem, specially if you're using a development environment with hot reload (like most React frameworks).\n\nIf you want to cache samples on the browser you can use a `CacheStorage` object:\n\n```ts\nimport { SplendidGrandPiano, CacheStorage } from \"smplr\";\n\nconst context = new AudioContext();\nconst storage = new CacheStorage();\n// First time the instrument loads, will fetch the samples from http. Subsequent times from cache.\nconst piano = new SplendidGrandPiano(context, { storage });\n```\n\n⚠️ `CacheStorage` is based on [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) and only works in secure environments that runs with `https`. Read your framework documentation for setup instructions. For example, in nextjs you can use https://www.npmjs.com/package/next-dev-https. For vite there's https://github.com/liuweiGL/vite-plugin-mkcert. Find the appropriate solution for your environment.\n\n## Instruments\n\n### Sampler\n\nAn audio buffer sampler. Pass a `buffers` object with the files to be load:\n\n```js\nimport { Sampler } from \"smplr\";\n\nconst buffers = {\n  kick: \"https://smpldsnds.github.io/drum-machines/808-mini/kick.m4a\",\n  snare: \"https://smpldsnds.github.io/drum-machines/808-mini/snare-1.m4a\",\n};\nconst sampler = new Sampler(new AudioContext(), { buffers });\n```\n\nAnd then use the name of the buffer as note name:\n\n```js\nsampler.start({ note: \"kick\" });\n```\n\n### Soundfont\n\nA Soundfont player. By default it loads audio from Benjamin Gleitzman's package of\n[pre-rendered sound fonts](https://github.com/gleitz/midi-js-soundfonts).\n\n```js\nimport { Soundfont, getSoundfontNames, getSoundfontKits } from \"smplr\";\n\nconst marimba = new Soundfont(new AudioContext(), { instrument: \"marimba\" });\nmarimba.start({ note: \"C4\" });\n```\n\nIt's intended to be a modern replacement of [soundfont-player](https://github.com/danigb/soundfont-player)\n\n#### Soundfont instruments and kits\n\nUse `getSoundfontNames` to get all available instrument names and `getSoundfontKits` to get kit names.\n\nThere are two kits available: `MusyngKite` or `FluidR3_GM`. The first one is used by default: it sounds better but samples weights more.\n\n```js\nconst marimba = new Soundfont(context, {\n  instrument: \"clavinet\",\n  kit: \"FluidR3_GM\", // \"MusyngKite\" is used by default if not specified\n});\n```\n\nAlternatively, you can pass your custom url as the instrument. In that case, the `kit` is ignored:\n\n```js\nconst marimba = new Soundfont(context, {\n  instrumentUrl:\n    \"https://gleitz.github.io/midi-js-soundfonts/MusyngKite/marimba-mp3.js\",\n});\n```\n\n#### Soundfont sustained notes\n\nYou can enable note looping to make note names indefinitely long by loading loop data:\n\n```js\nconst marimba = new Soundfont(context, {\n  instrument: \"cello\",\n  loadLoopData: true,\n});\n```\n\n⚠️ This feature is still experimental and can produces clicks on lot of instruments.\n\n### SplendidGrandPiano\n\nA sampled acoustic piano. It uses Steinway samples with 4 velocity groups from\n[SplendidGrandPiano](https://github.com/sfzinstruments/SplendidGrandPiano)\n\n```js\nimport { SplendidGrandPiano } from \"smplr\";\n\nconst piano = new SplendidGrandPiano(new AudioContext());\n\npiano.start({ note: \"C4\" });\n```\n\n#### SplendidGrandPiano constructor\n\nThe second argument of the constructor accepts the following options:\n\n- `baseUrl`:\n- `detune`: global detune in cents (0 if not specified)\n- `velocity`: default velocity (100 if not specified)\n- `volume`: default volume (100 if not specified)\n- `decayTime`: default decay time (0.5 seconds)\n- `notesToLoad`: an object with the following shape: `{ notes: number[], velocityRange: [number, number]}` to specify a subset of notes to load\n\nExample:\n\n```ts\nconst piano = new SplendidGrandPiano(context, {\n  detune: -20,\n  volume: 80,\n  notesToLoad: {\n    notes: [60],\n    velocityRange: [1, 127],\n  },\n});\n```\n\n### Electric Piano\n\nA sampled electric pianos. Samples from https://github.com/sfzinstruments/GregSullivan.E-Pianos\n\n```js\nimport { ElectricPiano, getElectricPianoNames } from \"smplr\";\n\nconst instruments = getElectricPianoNames(); // =\u003e [\"CP80\", \"PianetT\", \"WurlitzerEP200\"]\n\nconst epiano = new ElectricPiano(new AudioContext(), {\n  instrument: \"PianetT\",\n});\n\nepiano.start({ note: \"C4\" });\n\n// Includes a (basic) tremolo effect:\nepiano.tremolo.level(30);\n```\n\nAvailable instruments:\n\n- `CP80`: Yamaha CP80 Electric Grand Piano v1.3 (29-Sep-2004)\n- `PianetT`: Hohner Pianet T (type 2) v1.3 (24-Sep-2004)\n- `WurlitzerEP200`: Wurlitzer EP200 Electric Piano v1.1 (16-May-1999)\n\n### Mallets\n\nSamples from [The Versilian Community Sample Library](https://github.com/sgossner/VCSL)\n\n```js\nimport { Mallet, getMalletNames } from \"smplr\";\n\nconst instruments = getMalletNames();\n\nconst mallet = new Mallet(new AudioContext(), {\n  instrument: instruments[0],\n});\n```\n\n### Mellotron\n\nSamples from [archive.org](https://archive.org/details/mellotron-archive-cd-rom-nki-wav.-7z)\n\n```js\nimport { Mellotron, getMellotronNames } from \"smplr\";\n\nconst instruments = getMellotronNames();\n\nconst mallet = new Mellotron(new AudioContext(), {\n  instrument: instruments[0],\n});\n```\n\n### Drum Machines\n\nSampled drum machines. Samples from different sources:\n\n```js\nimport { DrumMachine, getDrumMachineNames } from \"smplr\";\n\nconst instruments = getDrumMachineNames();\n\nconst context = new AudioContext();\nconst drums = new DrumMachine(context, { instrument: \"TR-808\" });\ndrums.start({ note: \"kick\" });\n\n// Drum samples are grouped and can have sample variations:\ndrums.getSampleNames(); // =\u003e ['kick-1', 'kick-2', 'snare-1', 'snare-2', ...]\ndrums.getGroupNames(); // =\u003e ['kick', 'snare']\ndrums.getSampleNamesForGroup(\"kick\") =\u003e // =\u003e ['kick-1', 'kick-2']\n\n// You can trigger samples by group name or specific sample\ndrums.start(\"kick\"); // Play the first sample of the group\ndrums.start(\"kick-1\"); // Play this specific sample\n```\n\n### Smolken double bass\n\n```js\nimport { Smolken, getSmolkenNames } from \"smplr\";\n\nconst instruments = getSmolkenNames(); // =\u003e Arco, Pizzicato \u0026 Switched\n\n// Create an instrument\nconst context = new AudioContext();\nconst doubleBass = await new Smolken(context, { instrument: \"Arco\" }).load;\n```\n\n### Versilian\n\nVersilian is a sample capable of using the [Versilian Community Sample Library](https://github.com/sgossner/VCSL).\n\n⚠️ Not all features are implemented. Some instruments may sound incorrect ⚠️\n\n```js\nimport { Versilian, getVersilianInstruments } from \"smplr\";\n\n// getVersilianInstruments returns a Promise\nconst instrumentNames = await getVersilianInstruments();\n\nconst context = new AudioContext();\nconst sampler = new Versilian(context, { instrument: instrumentNames[0] });\n```\n\n### Soundfont2Sampler\n\nSampler capable of reading .sf2 files directly:\n\n```ts\nimport { Soundfont2Sampler } from \"smplr\";\nimport { SoundFont2 } from \"soundfont2\";\n\nconst context = new AudioContext();\nconst sampler = new Soundfont2Sampler(context, {\n  url: \"https://smpldsnds.github.io/soundfonts/soundfonts/galaxy-electric-pianos.sf2\",\n  createSoundfont: (data) =\u003e new SoundFont2(data),\n});\n\nsampler.load.then(() =\u003e {\n  // list all available instruments for the soundfont\n  console.log(sampler.instrumentNames);\n\n  // load the first available instrument\n  sampler.loadInstrument(sampler.instrumentNames[0]);\n});\n```\n\nStill limited support. API may vary.\n\n## License\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanigb%2Fsmplr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanigb%2Fsmplr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanigb%2Fsmplr/lists"}