svelte-attach-sound 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # svelte-attach-sound
2
+
3
+ A Svelte attachment for binding sound playback to DOM events using the [Svelte 5 attachments API](https://svelte.dev/docs/svelte/attachments).
4
+
5
+ Uses [Howler.js](https://howlerjs.com/) as a peer dependency.
6
+
7
+ Find CC-Zero licensed sounds at [freesound.org](https://freesound.org/)
8
+
9
+ ## Installation
10
+
11
+ ```
12
+ npm install svelte-attach-sound howler
13
+ ```
14
+
15
+ ## Example
16
+
17
+ ```svelte
18
+ <script lang="ts">
19
+ import { sound, useSound } from "svelte-attach-sound";
20
+ import click_mp3 from "$lib/assets/click.mp3";
21
+
22
+ const click = useSound(click_mp3, ["pointerdown"]);
23
+ </script>
24
+
25
+ <!-- Inline -->
26
+ <button {@attach sound({ src: click_mp3, events: ["click"] })}>Click</button>
27
+
28
+ <!-- Factory: reusable with shared defaults -->
29
+ <button {@attach click()}>Click</button>
30
+ <button {@attach click({ volume: 0.5 })}>Click (quieter)</button>
31
+ ```
32
+
33
+ [Demo](https://joknoll.github.io/svelte-attach-sound/) | [npm](https://www.npmjs.com/package/svelte-attach-sound)
34
+
35
+ ## API
36
+
37
+ ### `sound(options)`
38
+
39
+ Svelte attachment that plays a sound on a DOM event.
40
+
41
+ | Option | Type | Required | Description |
42
+ | --------- | -------------------------------------------------------------- | -------- | -------------------------------------------------- |
43
+ | `src` | `string \| string[]` | Yes | Audio file URL(s), with fallbacks |
44
+ | `events` | `[playEvent, stopEvent?]` | Yes | DOM event to trigger play, and optionally stop |
45
+ | `...rest` | [`HowlOptions`](https://github.com/goldfire/howler.js#options) | No | Any Howler option (`volume`, `loop`, `rate`, etc.) |
46
+
47
+ ### `useSound(src, events, options?)`
48
+
49
+ Factory that returns a reusable attachment with preset defaults. The returned function accepts optional per-call overrides.
50
+
51
+ ```svelte
52
+ <script lang="ts">
53
+ const click = useSound(click_mp3, ["pointerdown"], { volume: 0.8 });
54
+ </script>
55
+
56
+ <button {@attach click()}>Uses defaults</button>
57
+ <button {@attach click({ volume: 0.3 })}>Override volume</button>
58
+ ```
59
+
60
+ ### `Sound` class
61
+
62
+ For manual control outside of attachments:
63
+
64
+ ```ts
65
+ import { Sound } from "svelte-attach-sound";
66
+
67
+ const s = new Sound(click_mp3, { volume: 0.5 });
68
+ s.play();
69
+ s.stop();
70
+ s.destroy(); // stops playback and frees resources
71
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,105 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/index.ts
3
+ /**
4
+ * A class representing a synthetic sound.
5
+ * Can be used standalone for programmatic playback without any DOM dependency.
6
+ */
7
+ var Sound = class {
8
+ config;
9
+ howl;
10
+ ready;
11
+ constructor(src, options = {}) {
12
+ this.config = {
13
+ ...options,
14
+ src
15
+ };
16
+ this.ready = this.create();
17
+ }
18
+ whenReady(fn) {
19
+ this.ready.then(fn).catch(() => {});
20
+ }
21
+ async create() {
22
+ const { Howl } = await import("howler/src/howler.core");
23
+ this.howl = new Howl(this.config);
24
+ }
25
+ play() {
26
+ this.whenReady(() => this.howl?.play());
27
+ }
28
+ stop() {
29
+ this.whenReady(() => this.howl?.stop());
30
+ }
31
+ destroy() {
32
+ this.whenReady(() => {
33
+ this.howl?.stop();
34
+ this.howl?.unload();
35
+ });
36
+ }
37
+ };
38
+ /**
39
+ * Creates a sound attachment that binds playback to DOM events on the element.
40
+ *
41
+ * Runs inside a Svelte effect — if options contain reactive state, the attachment
42
+ * will automatically tear down and recreate when that state changes.
43
+ *
44
+ * @param options Options including `src`, `events`, and any Howler options.
45
+ * @returns A Svelte attachment.
46
+ *
47
+ * @example
48
+ * ```svelte
49
+ * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
50
+ * Click me
51
+ * </button>
52
+ * ```
53
+ */
54
+ function sound(options) {
55
+ return (element) => {
56
+ const { src, events, ...howlOptions } = options;
57
+ const [playEvent, stopEvent] = events;
58
+ const instance = new Sound(src, howlOptions);
59
+ const handlePlay = () => instance.play();
60
+ const handleStop = () => instance.stop();
61
+ element.addEventListener(playEvent, handlePlay);
62
+ if (stopEvent) element.addEventListener(stopEvent, handleStop);
63
+ return () => {
64
+ element.removeEventListener(playEvent, handlePlay);
65
+ if (stopEvent) element.removeEventListener(stopEvent, handleStop);
66
+ instance.destroy();
67
+ };
68
+ };
69
+ }
70
+ /**
71
+ * Creates a pre-configured sound attachment factory that can be reused across
72
+ * multiple elements with optional per-element overrides.
73
+ *
74
+ * @param src The source URL(s) of the sound.
75
+ * @param events The `[playEvent, stopEvent?]` tuple.
76
+ * @param options Optional base Howler options.
77
+ * @returns A factory function that returns a Svelte attachment.
78
+ *
79
+ * @example
80
+ * ```svelte
81
+ * <script>
82
+ * import { useSound } from "svelte-attach-sound";
83
+ * import click_mp3 from "./assets/click.mp3";
84
+ *
85
+ * const click = useSound(click_mp3, ["click"]);
86
+ * <\/script>
87
+ *
88
+ * <button {@attach click()}>Click me</button>
89
+ *
90
+ * <!-- Override options per-element -->
91
+ * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
92
+ * ```
93
+ */
94
+ function useSound(src, events, options) {
95
+ return (overrideOptions) => sound({
96
+ src,
97
+ events,
98
+ ...options,
99
+ ...overrideOptions
100
+ });
101
+ }
102
+ //#endregion
103
+ exports.Sound = Sound;
104
+ exports.sound = sound;
105
+ exports.useSound = useSound;
@@ -0,0 +1,72 @@
1
+ import { HowlOptions } from "howler";
2
+ import { Attachment } from "svelte/attachments";
3
+
4
+ //#region src/index.d.ts
5
+ type SoundSource = HowlOptions["src"];
6
+ type SoundEvents = [keyof HTMLElementEventMap, (keyof HTMLElementEventMap)?];
7
+ type SoundOptions = Omit<HowlOptions, "src">;
8
+ /**
9
+ * Options for the sound attachment factory.
10
+ */
11
+ type Options = {
12
+ events: SoundEvents;
13
+ } & HowlOptions;
14
+ /**
15
+ * A class representing a synthetic sound.
16
+ * Can be used standalone for programmatic playback without any DOM dependency.
17
+ */
18
+ declare class Sound {
19
+ private config;
20
+ private howl;
21
+ private ready;
22
+ constructor(src: SoundSource, options?: SoundOptions);
23
+ private whenReady;
24
+ private create;
25
+ play(): void;
26
+ stop(): void;
27
+ destroy(): void;
28
+ }
29
+ /**
30
+ * Creates a sound attachment that binds playback to DOM events on the element.
31
+ *
32
+ * Runs inside a Svelte effect — if options contain reactive state, the attachment
33
+ * will automatically tear down and recreate when that state changes.
34
+ *
35
+ * @param options Options including `src`, `events`, and any Howler options.
36
+ * @returns A Svelte attachment.
37
+ *
38
+ * @example
39
+ * ```svelte
40
+ * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
41
+ * Click me
42
+ * </button>
43
+ * ```
44
+ */
45
+ declare function sound(options: Options): Attachment<HTMLElement>;
46
+ /**
47
+ * Creates a pre-configured sound attachment factory that can be reused across
48
+ * multiple elements with optional per-element overrides.
49
+ *
50
+ * @param src The source URL(s) of the sound.
51
+ * @param events The `[playEvent, stopEvent?]` tuple.
52
+ * @param options Optional base Howler options.
53
+ * @returns A factory function that returns a Svelte attachment.
54
+ *
55
+ * @example
56
+ * ```svelte
57
+ * <script>
58
+ * import { useSound } from "svelte-attach-sound";
59
+ * import click_mp3 from "./assets/click.mp3";
60
+ *
61
+ * const click = useSound(click_mp3, ["click"]);
62
+ * </script>
63
+ *
64
+ * <button {@attach click()}>Click me</button>
65
+ *
66
+ * <!-- Override options per-element -->
67
+ * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
68
+ * ```
69
+ */
70
+ declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
71
+ //#endregion
72
+ export { Sound, sound, useSound };
@@ -0,0 +1,72 @@
1
+ import { HowlOptions } from "howler";
2
+ import { Attachment } from "svelte/attachments";
3
+
4
+ //#region src/index.d.ts
5
+ type SoundSource = HowlOptions["src"];
6
+ type SoundEvents = [keyof HTMLElementEventMap, (keyof HTMLElementEventMap)?];
7
+ type SoundOptions = Omit<HowlOptions, "src">;
8
+ /**
9
+ * Options for the sound attachment factory.
10
+ */
11
+ type Options = {
12
+ events: SoundEvents;
13
+ } & HowlOptions;
14
+ /**
15
+ * A class representing a synthetic sound.
16
+ * Can be used standalone for programmatic playback without any DOM dependency.
17
+ */
18
+ declare class Sound {
19
+ private config;
20
+ private howl;
21
+ private ready;
22
+ constructor(src: SoundSource, options?: SoundOptions);
23
+ private whenReady;
24
+ private create;
25
+ play(): void;
26
+ stop(): void;
27
+ destroy(): void;
28
+ }
29
+ /**
30
+ * Creates a sound attachment that binds playback to DOM events on the element.
31
+ *
32
+ * Runs inside a Svelte effect — if options contain reactive state, the attachment
33
+ * will automatically tear down and recreate when that state changes.
34
+ *
35
+ * @param options Options including `src`, `events`, and any Howler options.
36
+ * @returns A Svelte attachment.
37
+ *
38
+ * @example
39
+ * ```svelte
40
+ * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
41
+ * Click me
42
+ * </button>
43
+ * ```
44
+ */
45
+ declare function sound(options: Options): Attachment<HTMLElement>;
46
+ /**
47
+ * Creates a pre-configured sound attachment factory that can be reused across
48
+ * multiple elements with optional per-element overrides.
49
+ *
50
+ * @param src The source URL(s) of the sound.
51
+ * @param events The `[playEvent, stopEvent?]` tuple.
52
+ * @param options Optional base Howler options.
53
+ * @returns A factory function that returns a Svelte attachment.
54
+ *
55
+ * @example
56
+ * ```svelte
57
+ * <script>
58
+ * import { useSound } from "svelte-attach-sound";
59
+ * import click_mp3 from "./assets/click.mp3";
60
+ *
61
+ * const click = useSound(click_mp3, ["click"]);
62
+ * </script>
63
+ *
64
+ * <button {@attach click()}>Click me</button>
65
+ *
66
+ * <!-- Override options per-element -->
67
+ * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
68
+ * ```
69
+ */
70
+ declare function useSound(src: SoundSource, events: SoundEvents, options?: SoundOptions): (overrideOptions?: Partial<Options>) => Attachment<HTMLElement>;
71
+ //#endregion
72
+ export { Sound, sound, useSound };
package/dist/index.mjs ADDED
@@ -0,0 +1,102 @@
1
+ //#region src/index.ts
2
+ /**
3
+ * A class representing a synthetic sound.
4
+ * Can be used standalone for programmatic playback without any DOM dependency.
5
+ */
6
+ var Sound = class {
7
+ config;
8
+ howl;
9
+ ready;
10
+ constructor(src, options = {}) {
11
+ this.config = {
12
+ ...options,
13
+ src
14
+ };
15
+ this.ready = this.create();
16
+ }
17
+ whenReady(fn) {
18
+ this.ready.then(fn).catch(() => {});
19
+ }
20
+ async create() {
21
+ const { Howl } = await import("howler/src/howler.core");
22
+ this.howl = new Howl(this.config);
23
+ }
24
+ play() {
25
+ this.whenReady(() => this.howl?.play());
26
+ }
27
+ stop() {
28
+ this.whenReady(() => this.howl?.stop());
29
+ }
30
+ destroy() {
31
+ this.whenReady(() => {
32
+ this.howl?.stop();
33
+ this.howl?.unload();
34
+ });
35
+ }
36
+ };
37
+ /**
38
+ * Creates a sound attachment that binds playback to DOM events on the element.
39
+ *
40
+ * Runs inside a Svelte effect — if options contain reactive state, the attachment
41
+ * will automatically tear down and recreate when that state changes.
42
+ *
43
+ * @param options Options including `src`, `events`, and any Howler options.
44
+ * @returns A Svelte attachment.
45
+ *
46
+ * @example
47
+ * ```svelte
48
+ * <button {@attach sound({ src: click_mp3, events: ["click"] })}>
49
+ * Click me
50
+ * </button>
51
+ * ```
52
+ */
53
+ function sound(options) {
54
+ return (element) => {
55
+ const { src, events, ...howlOptions } = options;
56
+ const [playEvent, stopEvent] = events;
57
+ const instance = new Sound(src, howlOptions);
58
+ const handlePlay = () => instance.play();
59
+ const handleStop = () => instance.stop();
60
+ element.addEventListener(playEvent, handlePlay);
61
+ if (stopEvent) element.addEventListener(stopEvent, handleStop);
62
+ return () => {
63
+ element.removeEventListener(playEvent, handlePlay);
64
+ if (stopEvent) element.removeEventListener(stopEvent, handleStop);
65
+ instance.destroy();
66
+ };
67
+ };
68
+ }
69
+ /**
70
+ * Creates a pre-configured sound attachment factory that can be reused across
71
+ * multiple elements with optional per-element overrides.
72
+ *
73
+ * @param src The source URL(s) of the sound.
74
+ * @param events The `[playEvent, stopEvent?]` tuple.
75
+ * @param options Optional base Howler options.
76
+ * @returns A factory function that returns a Svelte attachment.
77
+ *
78
+ * @example
79
+ * ```svelte
80
+ * <script>
81
+ * import { useSound } from "svelte-attach-sound";
82
+ * import click_mp3 from "./assets/click.mp3";
83
+ *
84
+ * const click = useSound(click_mp3, ["click"]);
85
+ * <\/script>
86
+ *
87
+ * <button {@attach click()}>Click me</button>
88
+ *
89
+ * <!-- Override options per-element -->
90
+ * <button {@attach click({ volume: 0.5 })}>Click me (quieter)</button>
91
+ * ```
92
+ */
93
+ function useSound(src, events, options) {
94
+ return (overrideOptions) => sound({
95
+ src,
96
+ events,
97
+ ...options,
98
+ ...overrideOptions
99
+ });
100
+ }
101
+ //#endregion
102
+ export { Sound, sound, useSound };
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "svelte-attach-sound",
3
+ "version": "0.1.1",
4
+ "description": "A Svelte attachment for binding sound playback to DOM events using Howler.js",
5
+ "keywords": [
6
+ "attachment",
7
+ "howler",
8
+ "sound",
9
+ "svelte",
10
+ "svelte5"
11
+ ],
12
+ "homepage": "https://github.com/joknoll/svelte-attach-sound#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/joknoll/svelte-attach-sound/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "joknoll",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/joknoll/svelte-attach-sound.git"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "type": "module",
26
+ "main": "./dist/index.cjs",
27
+ "module": "./dist/index.mjs",
28
+ "types": "./dist/index.d.cts",
29
+ "exports": {
30
+ ".": {
31
+ "import": "./dist/index.mjs",
32
+ "require": "./dist/index.cjs"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "scripts": {
37
+ "build": "tsdown",
38
+ "dev": "tsdown --watch",
39
+ "test": "vitest",
40
+ "typecheck": "tsc --noEmit",
41
+ "prepublishOnly": "bun run build"
42
+ },
43
+ "devDependencies": {
44
+ "@types/howler": "^2.2.12",
45
+ "@types/node": "^25.0.3",
46
+ "bumpp": "^10.3.2",
47
+ "howler": "^2.2.4",
48
+ "oxfmt": "^0.40.0",
49
+ "oxlint": "^1.50.0",
50
+ "svelte": "^5.53.11",
51
+ "tsdown": "^0.21.2",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^4.0.16"
54
+ },
55
+ "peerDependencies": {
56
+ "howler": ">=2.0.0",
57
+ "svelte": ">=5.53.11"
58
+ }
59
+ }