A music player that connects to your cloud/distributed storage.
1---
2layout: layouts/diffuse.vto
3
4styles:
5 - styles/base.css
6 - styles/diffuse/page.css
7 - vendor/@phosphor-icons/bold/style.css
8 - vendor/@phosphor-icons/fill/style.css
9
10scripts:
11 - index.js
12
13# ELEMENTS
14
15configurators:
16 - url: "components/configurator/input/element.js"
17 title: "Input"
18 desc: "Allows for multiple inputs to be used at once."
19 - url: "components/configurator/output/element.js"
20 title: "Output"
21 desc: "Enables the user to configure a specific output. If no default output is set, it creates a temporary session by storing everything in memory."
22 - url: "components/configurator/scrobbles/element.js"
23 title: "Scrobbles"
24 desc: "Configure multiple scrobblers (music trackers)."
25 todo: true
26
27engines:
28 - url: "components/engine/audio/element.js"
29 title: "Audio"
30 desc: "Plays audio through audio elements."
31 - url: "components/engine/queue/element.js"
32 title: "Queue"
33 desc: "A queue for tracks."
34 - url: "components/engine/repeat-shuffle/element.js"
35 title: "Repeat & Shuffle"
36 desc: "Signals synced with local storage (classified by group) that decide if audio should be repeated and if the queue should be shuffled when filling it."
37 - url: "components/engine/scope/element.js"
38 title: "Scope"
39 desc: >
40 Signals that could influence the scope of a set of tracks.
41
42input:
43 - url: "components/input/https/element.js"
44 title: "HTTPS"
45 desc: >
46 HTTPS URLs to audio files or streams.
47 - title: "HTTPS (JSON)"
48 desc: >
49 Generate tracks based on HTTPS servers that provide JSON (directory) listings.
50 todo: true
51 - url: "components/input/local/element.js"
52 title: "Local"
53 desc: >
54 Audio files or directories from your local device, using the browser's File System Access API.
55 - url: "components/input/opensubsonic/element.js"
56 title: "Opensubsonic"
57 desc: >
58 Add any (open)subsonic server.
59 - url: "components/input/s3/element.js"
60 title: "S3"
61 desc: >
62 AWS S3 and services that provide the same surface API such as Cloudflare R2.
63 - title: "WebDAV"
64 desc: >
65 Add any WebDAV server.
66 todo: true
67
68orchestrators:
69 - url: "components/orchestrator/auto-queue/element.js"
70 title: "Automatic queue"
71 desc: >
72 Fill the queue automatically with non-manual items (shuffled or regular, based on repeat-shuffle engine).
73 - url: "components/orchestrator/favourites/element.js"
74 title: "Favourites"
75 desc: >
76 Mark tracks as favourites. Automatically creates an unordered 'Favourites' playlist.
77 - url: "components/orchestrator/input/element.js"
78 title: "Input"
79 desc: "**A default input configuration.** Contains all the inputs provided here."
80 - url: "components/orchestrator/media-session/element.js"
81 title: "Media Session"
82 desc: "Keeps the browser/os media session in sync with queue and audio state. Adds handlers for previous, next, seek to, etc."
83 - url: "components/orchestrator/output/element.js"
84 title: "Output"
85 desc: "**A default output configuration.** Contains all the outputs provided here along with the relevant transformers."
86 - url: "components/orchestrator/process-tracks/element.js"
87 title: "Process inputs into tracks"
88 desc: "Whenever the cached tracks are initially loaded through the passed output element it will list tracks by using the passed input element. Afterwards it loops over all tracks and checks if metadata needs to be fetched. If anything has changed, it'll pass the results to the output element."
89 - url: "components/orchestrator/queue-audio/element.js"
90 title: "Queue ⭤ Audio"
91 desc: "Connects the given queue engine to the given audio engine."
92 - url: "components/orchestrator/sources/element.js"
93 title: "Sources"
94 desc: "Monitor tracks from the given output to form a list of sources based on the input's sources return value."
95 - url: "components/orchestrator/scoped-tracks/element.js"
96 title: "Scoped Tracks"
97 desc: "Supplies the tracks from the given output to the given search processor whenever the tracks collection changes. Additionally it can perform a search and other ways to reduce the scope of tracks based on the given scope engine. Provides a `tracks` signal similar to `output.tracks.collection`"
98
99output:
100 - url: "components/output/polymorphic/indexed-db/element.js"
101 title: "Polymorphic / IndexedDB"
102 desc: "Stores output into the local indexedDB. Supports any type of data that indexedDB supports."
103 - url: "components/output/bytes/s3/element.js"
104 title: "Bytes / S3"
105 desc: >
106 Store output data on AWS S3 or compatible services such as Cloudflare R2.
107 - url: "components/output/raw/atproto/element.js"
108 title: "Raw / AT Protocol"
109 desc: >
110 Store your user data on the storage associated with your ATProtocol identity. Data is lexicon shaped by default so this element takes in that data directly without any transformations.
111
112processors:
113 - url: "components/processor/artwork/element.js"
114 title: "Artwork"
115 desc: "Fetches cover art for a given set of tracks, stored locally in indexedDB. Checks the audio metadata first, then MusicBrainz and uses Last.fm as the fallback."
116 - url: "components/processor/metadata/element.js"
117 title: "Metadata"
118 desc: "Fetch audio metadata for a given set of tracks, adding to the `Track` object."
119 - url: "components/processor/search/element.js"
120 title: "Search"
121 desc: "Provides a way to search through a collection of tracks, powered by orama.js"
122
123supplements:
124 - title: "Last.fm scrobbler"
125 todo: true
126 - title: "ListenBrainz scrobbler"
127 todo: true
128 - title: "Rocksky scrobbler"
129 todo: true
130 - title: "Teal.fm scrobbler"
131 todo: true
132
133transformers:
134 - title: "Output / Bytes / Automerge"
135 desc: "Translate data to and from an Automerge CRDT."
136 url: "components/transformer/output/bytes/automerge/element.js"
137 todo: true
138 - title: "Output / Bytes / Cambria lenses"
139 desc: "Uses the Cambria library to seamlessly translate between data schemas so that no data migration is needed."
140 todo: true
141 - title: "Output / Bytes / DASL Sync"
142 desc: "Syncs data between local and remote using CID-based diffing and performs union merges with tombstone tracking when both sides have diverged."
143 url: "components/transformer/output/bytes/dasl-sync/element.js"
144 - title: "Output / Bytes / JSON"
145 desc: "Raw data schema output ⇄ JSON Uint8Array."
146 url: "components/transformer/output/bytes/json/element.js"
147 - title: "Output / Raw / AT Protocol Sync"
148 desc: "Wraps an AT Protocol output with a local IndexedDB cache. Uses the repo revision to skip unnecessary fetches and performs union merges with tombstone tracking when both local and remote have diverged."
149 url: "components/transformer/output/raw/atproto-sync/element.js"
150 - title: "Output / Refiner / Default"
151 desc: "The task of a refiner transformer is to remove the output state that is not meant to be saved to storage. For example, ephemeral tracks; this transformer will keep them in memory, but they will not be present in the output. **Ideally this is part of every theme, but you may swap it out with another transformer that might provide better defaults.**"
152 url: "components/transformer/output/refiner/default/element.js"
153 - title: "Output / Refiner / Track URI Passkey"
154 desc: "Encrypts track URIs using a passkey-derived PRF key. On read, decrypts `encrypted://` URIs transparently; on write, re-encrypts all URIs before passing downstream. Tracks that cannot be decrypted are held separately and excluded from the visible collection."
155 url: "components/transformer/output/refiner/track-uri-passkey/element.js"
156 - title: "Output / String / JSON"
157 desc: "Raw data schema output ⇄ JSON UTF8 string."
158 url: "components/transformer/output/string/json/element.js"
159
160# DEFINITIONS
161
162definitions:
163 - title: "Output / Collaboration"
164 desc: >
165 Represents a collaboration between multiple collaborators on a subject, such as a playlist.
166 url: "definitions/output/collaboration.json"
167 - title: "Output / Facet"
168 desc: >
169 Facet pointer or HTML snippet.
170 url: "definitions/output/facet.json"
171 - title: "Output / Playlist Item"
172 desc: >
173 Represents a single item in a playlist. Tracks are matched based on the given criteria. A playlist is formed by grouping items by their playlist property.
174 url: "definitions/output/playlistItem.json"
175 - title: "Output / Playlist Item Bundle"
176 desc: >
177 A bundle of playlist items.
178 url: "definitions/output/playlistItemBundle.json"
179 - title: "Output / Progress"
180 desc: >
181 Used to track progress of (long) audio playback.
182 todo: true
183 - title: "Output / Theme"
184 desc: >
185 Theme pointer or HTML snippet.
186 url: "definitions/output/theme.json"
187 - title: "Output / Track"
188 desc: >
189 Represents audio that can be played, or a placeholder for a source of tracks. Contains a URI that will resolve to the audio.
190 url: "definitions/output/track.json"
191 - title: "Output / Track Bundle"
192 desc: >
193 A bundle of tracks.
194 url: "definitions/output/trackBundle.json"
195
196---
197
198<header>
199 <div>
200 <h1 class="diffuse-logo">
201 {{ await comp.diffuse.logo() }}
202 <span id="status">
203 <a href="/latest/" title="Upgrade to latest" class="animate-spin">
204 <i class="ph-bold ph-spinner"></i>
205 </a>
206 <a href="/chronicle/" title="Browse versions of Diffuse">
207 <i class="ph-fill ph-star-four"></i>
208 </a>
209 </span>
210 </h1>
211 <p class="construct dither-mask">
212 Your audio<br />your data<br />your rules
213 </p>
214 <p>
215 Diffuse is a collection of components and software that make it possible to listen to audio from various sources on your devices and the web, and to create the ideal digital audio listening experience for you.
216 </p>
217 <p style="align-items: center; display: flex; gap: var(--space-2xs); opacity: 0.55;">
218 <i class="ph-fill ph-crane"></i>
219 <strong style="font-weight: 700;">WORK IN PROGRESS</strong>
220 </p>
221 <ul class="table-of-contents">
222 <li><a href="#usage">Usage</a></li>
223 <li><a href="#themes">Themes</a></li>
224 <li><a href="#facets">Facets</a></li>
225 <li><a href="#agency">Agency</a></li>
226 <li><a href="#elements">Elements</a></li>
227 <li><a href="#definitions">Definitions</a></li>
228 </ul>
229 <p>
230 <small>Built by <a href="https://tokono.ma">tokono.ma</a></small>
231 </p>
232 </div>
233 <div class="dither-mask filler"></div>
234</header>
235<main>
236 <!-- USAGE -->
237 <section>
238 <h2 id="usage">Usage</h2>
239
240 <div class="columns">
241 <div class="element">
242 <p>
243 The easiest way to start is by exploring the software. If you prefer a traditional pre­packaged application approach, you can check out <a href="themes/">themes</a>. Themes can look and function entirely different from each other, so make sure to explore!
244 </p>
245
246 <p>
247 Alternatively, there's <a href="facets/">facets</a> which allows you to use any component from any theme interchange­ably, among pieces that are not in themes; each in a separate browser tab. Each tab talks to each other, so you can for example browse audio in one tab and play it in another.
248 </p>
249 </div>
250
251 <div class="element">
252 <p>
253 <strong style="color: var(--accent); font-weight: 700;">Diffuse is not your typical streaming service though, you have to add sources of audio.</strong> To start you can try out the demo by clicking the button below, or, go to a <a href="themes/">theme</a> or <a href="facets/">facet</a> that lets you add your audio input.
254 </p>
255
256 <p>
257 <button id="add-sample-content">
258 <span>Add sample content</span>
259 </button>
260 </p>
261 </div>
262 </div>
263 </section>
264
265 <!-- THEMES & FACETS -->
266 <div class="columns">
267 <section>
268 <h2 id="themes">Themes</h2>
269
270 <p>
271 Themes each provide a traditional browser web application built on the Diffuse elements. They focus on delivering <strong>a great encompassing experience in a single window</strong>. They represent the opposite of facets, but can optionally delegate functionality to facets if they wish to do so.
272 </p>
273
274 <p>
275 <a class="button button--alt" href="themes/">Explore</a>
276 </p>
277 </section>
278
279 <section>
280 <h2 id="facets">Facets</h2>
281
282 <p>
283 Facets are various user interfaces, with their own logic, each loaded in their own web page. Every used Diffuse component is configured so that it operates in broadcast mode, making all the pages communicate with each other. <strong>You choose the features you want in your software.</strong>
284 </p>
285
286 <p>
287 <a class="button button--alt" href="facets/">Explore</a>
288 </p>
289 </section>
290 </div>
291
292 <!-- AGENCY -->
293 <section>
294 <h2 id="agency">Agency</h2>
295
296 <p>
297 The goal is for every user, no matter their knowledge level, to have agency over their data and their software. One can start with making small changes and gradually progress to making big changes.
298 </p>
299
300 <p>
301 You can store your user-data in various places, and easily export, import and sync it.
302 </p>
303
304 <h3>Take control</h3>
305
306 <ul class="columns">
307 <div class="element">
308 <!-- 1 -->
309 <li>
310 <p>
311 <i class="ph-fill ph-storefront"></i> <strong>Level 1</strong>: Pick your restaurant, food comes in all shapes and sizes. The equivalent of choosing a Diffuse <a href="themes/">theme</a>.
312 </p>
313 </li>
314 <!-- 2 -->
315 <li>
316 <p>
317 <i class="ph-fill ph-basket"></i> <strong>Level 2</strong>: Take out food from various places, eg. cheese shop + bakery. You choose how you combine the foods and from where you order them. That's <a href="facets/">facets</a>.
318 </p>
319 </li>
320 <!-- 3 -->
321 <li>
322 <p>
323 <i class="ph-fill ph-pepper"></i> <strong>Level 3</strong>: Now that you know which food is good and how to make combinations, you might make a slight customization, add a little something of your own (eg. add some spice). However, you're not quite confident enough which spice to pick, so you hire some help.
324 </p>
325 <p>
326 This can be done using the <a href="facets/#builder">facets builder</a> which allows you to build on top of a familiar preconfigured foundation and load custom facets. People might share their own, or maybe you use an LLM to generate something for you (eg. an album art gallery).
327 </p>
328 </li>
329 <!-- 4 -->
330 <li>
331 <p>
332 <i class="ph-fill ph-knife"></i> <strong>Level 4</strong>: You learned a bit from watching and talking to the help you hired, so you decide to take things in your own hands.
333 </p>
334 <p>
335 You continue to use the <a href="facets/#builder">facets builder</a> but learn a bit of <a href="https://htmlforpeople.com">HTML</a>, <a href="https://javascript.info">Javascript</a> and <a href="https://htmlforpeople.com/css-basics/">CSS</a>; so you're able to write your own facet.
336 </p>
337 </li>
338 </div>
339 <div class="element">
340 <!-- 5 -->
341 <li>
342 <p>
343 <i class="ph-fill ph-cooking-pot"></i> <strong>Level 5</strong>: At this point you're confident enough to make a meal from scratch. You can start very simple, eg. just throwing a steak in the pan with some butter and some salt to it. Then later add some baked potatoes and go from there.
344 </p>
345 <p>
346 A similar tool comes into play here, the <a href="themes/#builder">themes builder</a>. Same concept as the facets builder, but now you need to specify the foundation yourself. You can use the <a href="#elements">elements</a> listed below to do so. The code for these is available from this website or through the <a href="https://jsr.io/@toko/diffuse">Javascript package</a>.
347 </p>
348 </li>
349 <!-- 6 -->
350 <li>
351 <p>
352 <i class="ph-fill ph-storefront"></i> <strong>Level 6</strong>: You open your own restaurant.
353 </p>
354 <p>
355 You can host the theme you made on any web server, it's only HTML after all. Only difference is that you'll have to create the entire HTML tree, not just the body element, as is the case with the theme builder.
356 </p>
357 </li>
358 <!-- 7 -->
359 <li>
360 <p>
361 <i class="ph-fill ph-chef-hat"></i> <strong>Level 7</strong>: You got promoted to master chef. Time to open your own restaurant chain.
362 </p>
363 <p>
364 You can self-host Diffuse, it's <a href="https://tangled.org/tokono.ma/diffuse/blob/v4/">open-source</a>! Or you present your own collection of elements.
365 </p>
366 </li>
367 </div>
368 </ul>
369 </section>
370
371 <!-- ELEMENTS -->
372 <section>
373 <h2 id="elements">Elements</h2>
374
375 <p>
376 The (web) components of the system. These custom elements are then recombined into an entire music player and browser, or whatever you want to build.
377 </p>
378
379 <p>
380 Consume these using the facets <a href="facets/#builder">builder</a>, the Javascript <a href="https://jsr.io/@toko/diffuse">package</a>, or the linked Javascript files down below.
381 </p>
382
383 <div class="columns">
384 {{ await comp.element({
385 title: "Configurators",
386 items: configurators,
387 content: `
388 Elements that serve as an intermediate in order to make a particular kind of element configurable. In other words, these allow for an element to be swapped out with another that takes the same set of the actions and data output.
389 `
390 }) }}
391
392 {{ await comp.element({
393 title: "Engines",
394 items: engines,
395 content: `
396 Elements with each a singular purpose and don't have any UI. There are specialised UI and orchestrator elements that control these.
397 `
398 }) }}
399
400 {{ await comp.element({
401 title: "Input",
402 items: input,
403 content: `
404 Inputs are sources of audio tracks. Each track is an entry in the list of possible items to play. These can be files or streams, static or dynamic.
405 `
406 }) }}
407
408 {{ await comp.element({
409 title: "Orchestrators",
410 items: orchestrators,
411 content: `
412 These too are element compositions. However, unlike themes, these are purely logical. Mostly exist in order to construct sensible defaults to use across themes and other compositions.
413 `
414 }) }}
415
416 {{ await comp.element({
417 title: "Output",
418 items: output,
419 content: `
420 Output is application-derived data such as playlists. These elements can receive such data and keep it around. These are categorised by the type of data they ingest, or many types in the case of polymorphic. Optionally use transformers to convert output into the expected format.
421 `
422 }) }}
423
424 {{ await comp.element({
425 title: "Processors",
426 items: processors,
427 content: `
428 These elements work with the output generated by the input elements to add more data to them, or process them in some other way.
429 `
430 }) }}
431
432 {{ await comp.element({
433 title: "Supplements",
434 items: supplements,
435 content: `
436 Additional elements, such as scrobblers.
437 `
438 }) }}
439
440 {{ await comp.element({
441 title: "Transformers",
442 items: transformers,
443 content: `
444 Transform data from one format or schema into another. See schema section below for more information. Just as configurators, these are intermediates and require to have the same set of actions as the element it targets.
445 `
446 }) }}
447 </div>
448 </section>
449
450 <!-- DEFINITIONS -->
451 <section>
452 <h2 id="definitions">Definitions</h2>
453
454 <p>All of the elements here are built with these data definitions in mind. That said, you can mix elements that use different definitions; you just have to put a transformer between them in order to translate between them, if needed.</p>
455
456 {{ await comp.list({ items: definitions }) }}
457 </section>
458</main>