# Audio Worklet example

**URL:** <https://talk.observablehq.com/t/audio-worklet-example/5138>\
**Category:** Show and tell\
**Created:** [May 31, 2021, 11:00pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138 "2021-05-31T23:00:41Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![skybrian](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/skybrian/32/6516_2.png) [@skybrian](https://talk.observablehq.com/u/skybrian)\
**Post date:** [May 31, 2021, 11:00pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/1 "2021-05-31T23:00:41Z")

</div>

It’s possible to get an audio worklet running nicely in Observable, but it takes a few workarounds. Here’s how I did it:

> **[Audio Worklet Example](https://observablehq.com/@skybrian/audio-worklet-example)**
>
> If this demo is working, you should hear some white noise after moving the volume slider to the right. (As of May 2021, this demo doesn't work in Safari because it doesn't support audio worklets yet.) How it works For smooth performance, code that...

This might be a little nicer if the Observable editor somehow supported cells containing an embedded script, instead of a bare string.

Also, the trick of returning a Promise that never resolves to prevent downstream cells from running early seems a bit obscure. Maybe there’s a more elegant way to do it?

Edit: republished after I found a better way to convert a string to a URL.

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [June 1, 2021, 9:00am UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/2 "2021-06-01T09:00:40Z")

</div>

Awesome that’s cool!

Yeah we need a better way for embedded scripts. I had a similar situation popup when making a Service Worker. I tried wrapping the raw script in a function, then toString the function and chopping the function framing off with substring. You can keep syntax highlighting then. See [Offline embedded notebooks / Tom Larkworthy / Observable](https://observablehq.com/@tomlarkworthy/offline#offline) does it for a service worker. Not ideal though, and very fragile.

I throw off unresolved promises sometimes. Actually there are a few promise helpers in Promises.XXXX I think a Promises.never() would be a good additional. OK I made a PR as it seems like the right thing to me [Add promises.never as alternative to returning an unresolved promise. by tomlarkworthy · Pull Request #218 · observablehq/stdlib · GitHub](https://github.com/observablehq/stdlib/pull/218)

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [June 2, 2021, 7:46am UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/3 "2021-06-02T07:46:34Z")

</div>

Mike Bostock suggested using the invalidation promise instead of a never resolved one which is much less dangerous, and seems to fit our use case of holding up a dataflow indefinitely.

The nice thing with the invalidation promise is that it will resolve if their is an upstream dependant change or code change, but of course you can keep returning it to hold off downstream indefinitely. So it kinda perfectly fits the gap we are plugging, and you can’t really accidentally cause a permenant resource leak.

---

<div class="post-metadata">

**Author:** ![radames](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/radames/32/1699_2.png) [@radames](https://talk.observablehq.com/u/radames)\
**Post date:** [June 2, 2021, 7:52pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/4 "2021-06-02T19:52:40Z")

</div>

I’m curious if this wouldn’t be a good use case for `this` on Observable? where it stores the cell’s previously returned value.

```auto
audioContext = {
  let ctx = this || {}
  if(ctx.constructor.name === "AudioContext") return ctx

  ctx = new (window.AudioContext || window.webkitAudioContext)();
  invalidation.then(() => ctx.close());
  
  await ctx.audioWorklet.addModule(scriptUrl);
  return ctx;
}

```

more about `this`

> **[What's this?](https://observablehq.com/@fil/whats-this)**
>
> this is the cell's previously returned value: click “Replay” to use it. Click “Run cell ” to start with a fresh (undefined) this. Note: if the question is not about this observable cells’ this but about a the more generic JavaScript functions’ this,...

> **[this values in Observable](https://observablehq.com/@tmcw/untitled/5)**
>
> The value of this has long been a weird detail of JavaScript - sometimes this is: An element, when the function is an event handler A class, if the function is attached to one The window object, if the function isn't attached to anything this is sort...

---

<div class="post-metadata">

**Author:** ![skybrian](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/skybrian/32/6516_2.png) [@skybrian](https://talk.observablehq.com/u/skybrian)\
**Post date:** [June 2, 2021, 8:57pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/5 "2021-06-02T20:57:11Z")

</div>

Interesting idea, but it seems tricky to combine with invalidation. We need to make sure that a closed AudioContext is never reused.

I suppose we could just never close it? But there are cases when we do want to recreate the AudioContext, like after editing the code in the worklet.

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [June 2, 2021, 9:10pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/6 "2021-06-02T21:10:21Z")

</div>

Instead of a mutable you can use a coroutine-like yield [the power of js generators / Anjana Vakil / Observable](https://observablehq.com/@anjana/the-power-of-js-generators) which is quite good for sequential stuff.

---

<div class="post-metadata">

**Author:** ![skybrian](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/skybrian/32/6516_2.png) [@skybrian](https://talk.observablehq.com/u/skybrian)\
**Post date:** [June 2, 2021, 10:17pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/7 "2021-06-02T22:17:27Z")

</div>

I’m not sure how I would do that. The tricky bit is how to avoid yielding a new value for `running` when `volume` changes in a way that doesn’t change the value of `running`.

I tried this:

```auto
running = {
  if (this) {
    console.log("not changing running");
    return invalidation; // no changes once the previous value is true
  }
  let result = volume > 0;
  console.log("changing running to $result");
  return result;
}

```

However, it seems that returning invalidation causes the downstream audioContext to be closed without creating a new one. This isn’t quite the same as not yielding anything at all.

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [June 3, 2021, 8:28am UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/8 "2021-06-03T08:28:52Z")

</div>

here is how I replace the mutable with a yield ([Audio Worklet Example / Tom Larkworthy / Observable](https://observablehq.com/d/dfaca9e315497c9f#audioContext)) , with this construction you do not need to return invalidation promises, though you need to listen to the input change without going through dataflow so the cell does not reevaluate (ie. addEventListener to the viewof not the data stream)

I used to use mutables a fair amount but now I think I prefer this pattern as it avoid the ‘global’ though it does require more background knowledge to understand.

---

<div class="post-metadata">

**Author:** ![skybrian](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/skybrian/32/6516_2.png) [@skybrian](https://talk.observablehq.com/u/skybrian)\
**Post date:** [June 3, 2021, 8:07pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/9 "2021-06-03T20:07:56Z")

</div>

Interesting. The behavior is a little different, though. It works when you run the demo as-is, but for development, if you change the worklet code then it stops playing (due to invalidation cleanup) and doesn’t automatically restart. You have to wiggle the volume control to get it to go again.

---

<div class="post-metadata">

**Author:** ![tomlarkworthy](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/tomlarkworthy/32/5940_2.png) [@tomlarkworthy](https://talk.observablehq.com/u/tomlarkworthy)\
**Post date:** [June 3, 2021, 8:22pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/10 "2021-06-03T20:22:02Z")

</div>

For the specific case of audio context you can test if it is running ([Autoplay Policy Changes &nbsp;|&nbsp; Web &nbsp;|&nbsp; Google Developers](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes#webaudio))

So I fixed the example to provide the same behaviour by only awaiting for the input slider IF the context is not running. Thus if the cell is recomputed it skips the await.

---

<div class="post-metadata">

**Author:** ![radames](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/radames/32/1699_2.png) [@radames](https://talk.observablehq.com/u/radames)\
**Post date:** [June 3, 2021, 9:51pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/11 "2021-06-03T21:51:07Z")

</div>

perfect! this is a much better way to do it!

---

<div class="post-metadata">

**Author:** ![skybrian](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/skybrian/32/6516_2.png) [@skybrian](https://talk.observablehq.com/u/skybrian)\
**Post date:** [June 3, 2021, 11:42pm UTC](https://talk.observablehq.com/t/audio-worklet-example/5138/12 "2021-06-03T23:42:32Z")

</div>

Good find. I updated my example with this new approach.
