diff --git a/web-integration/html__events.md b/web-integration/html__events.md
new file mode 100644
index 0000000..e72d7e4
--- /dev/null
+++ b/web-integration/html__events.md
@@ -0,0 +1,431 @@
+# AsyncContext propagation with events in the HTML specification
+
+This document goes over the detailed consequence of applying the [event propagation principles](https://github.com/tc39/proposal-async-context/blob/master/WEB-INTEGRATION.md#summary-of-event-propagation-rules) to events defined by the HTML specification.
+
+To summarize the principles:
+- APIs that dispatch events synchronously do not change the current async context
+- events fired due to JS-external causes (e.g. user interaction) or due to cross-thread JS run in the empty context
+- APIs that asynchronously fire events due to a method/setter call, for which the event is fired either on
+ - an object created by the method
+ - on the object whose method was called, unless it's a singleton (e.g. `document`)
+
+ propagate the context from the method/setter call to the event dispatch
+- APIs that react to DOM tree changes (insertion/removal/move of children, ancestors, or attributes) propagate the context from the DOM update to the event fired
+- Other API shapes fire events in the empty context
+
+Unless otherwise specified by this document, events that are fired synchronously preserve the existing active AsyncContext, while events that are fired with no JavaScript on the stack are fired in the empty context.
+
+> [!IMPORTANT]
+> This document frequently uses wording similar to
+> > When X happens, the user agent must:
+> > - synchronously capture the current AsyncContext, and store it on the object
+> > - use that context to later fire the event triggered by the action
+>
+> It is possible that X happens multiple times synchronously after each other, which causes multiple contexts to be captured. Each of the fired events must then be fired in the corresponding context.
+>
+> In practice, the simpler implementation in many of those cases will be to capture the context in the scheduled task(s), rather than actually on the object itself. Another option is to store multiple contexts on the object, with some information to distinguish them when firing the event.
+
+## Section by section
+### 4.2.1 - The `style` element
+When [updating a `style` block](https://html.spec.whatwg.org/#the-style-element:update-a-style-block), the user agent must:
+- synchronously capture the current AsyncContext when one of the conditions to update a `style` block occurs, and store it on the `style` element
+- when obtaining the style sheet's critical subresources completes, use that context to fire the `load` or `error` event
+### 4.2.4, 4.6.6 - `link` elements of various types
+When [fetching and processing the linked resource](https://html.spec.whatwg.org/#fetch-and-process-the-linked-resource) the user agent must:
+- synchronously capture the current AsyncContext at the appropriate times to fetch and process the linked resource, and store it on the `link` element
+- use that context to fire the `load` or `error` events on that `link` element
+### 4.8.3, 4.8.4 - Images and the `img` element
+When an `img` element [is created or has experienced relevant mutations](https://html.spec.whatwg.org/multipage/images.html#when-to-obtain-images), the user agent must:
+- synchronously capture the current AsyncContext, and store it on the `img` element
+- use that context to fire the `load` or `error` events on that `img` element fired when [updating the image data](https://html.spec.whatwg.org/#update-the-image-data)
+
+When updating an `img` element to [react to changes in the environment](https://html.spec.whatwg.org/#img-environment-changes), the user agent must store an empty AsyncContext to be used to fire the `load` and `error` events.
+
+Note that AsyncContext propagation behaves the same regardless of whether an user agent obtains images immediately or on demand.
+### 4.8.5 - The `iframe` element
+When [processing the `iframe` attributes](https://html.spec.whatwg.org/#process-the-iframe-attributes), which can happen due to changes to its `src` and `srcdoc` attributes, or due to [it being connected or removed](https://html.spec.whatwg.org/#the-iframe-element:html-element-post-connection-steps), the user agent must:
+- synchronously capture the current AsyncContext, and store it on the `iframe` element
+- use that context to fire the `load` event on the `iframe` element
+> [!WARNING]
+ Firefox fires an unspecified `error` event fired: it should be fired in the same context as the `load` event would.
+
+Note that the captured context is not used for events fired inside the `iframe` as a consequence of the navigation.
+### 16.3.2 - Frames
+When [processing the `frame` attributes](https://html.spec.whatwg.org/#process-the-iframe-attributes), which can happen due to changes to its `src` attribute or due to [it being inserted](https://html.spec.whatwg.org/#frames:html-element-insertion-steps), the user agent must:
+- synchronously capture the current AsyncContext, and store it on the `frame` element
+- use that context to fire the `load` event on the `frame` element
+
+As with `iframe`, the captured context is not used for any event fired *inside* the framed document.
+### 4.8.6 - The `embed` element
+When an `embed` element [becomes potentially active](https://html.spec.whatwg.org/#concept-embed-active) while there is JavaScript on the stack, or it has it's `src`/`type` attributes set/changed/removed, the user agent must:
+- synchronously capture the current AsyncContext, and store it on the `embed` element (and *not* asynchronously in the [`embed` element setup steps](https://html.spec.whatwg.org/#the-embed-element-setup-steps))
+- use that context when firing the `load` event in the [`embed` element setup steps](https://html.spec.whatwg.org/#the-embed-element-setup-steps) or in the [completely finish loading a document](https://html.spec.whatwg.org/#completely-finish-loading) steps.
+
+If there is no JavaScript on the stack when the `embed` element [becomes potentially active](https://html.spec.whatwg.org/#concept-embed-active), the captured context is empty.
+### 4.8.7 - The `object` element
+When one of the conditions that cause the steps to determine what the `object` element represents [to be queued](https://html.spec.whatwg.org/#the-object-element:queue-an-element-task) occur, the user agent must:
+- synchronously capture the current AsyncContext, and store it on the `object` element (and *not* asynchronously in the queued steps)
+- use that context when firing the corresponding `load` and `error` events fired by the same algorithm
+
+If the condition that occurs is that the element changes between being rendered and not, the captured AsyncContext is empty.
+### 4.8.8 - The `video` element
+When firing a `resize` event due to the natural width or height of the video changing, use the empty context.
+### 4.8.11 - The media elements (shared logic for `video`/`audio`)
+Media elements have three different state classes, with event fired during transitions between the various states. These three classes are not independent, and changes in one can affect the others.
+
+> [!NOTE]
+> **INFO:** The next four sections go more in details into these states and events, and the last one into how they interact with AsyncContext propagation.
+#### The _network_ states
+They represent the loading progress of the associated resource, and whether loading is even possible at all. They are exposed as the `.networkState` attribute of the media element.
+
+Loading starts when first adding a `.src` / `.srcset` / `` to the media element, or when manually calling `.load()` on it, and it transitions between `NETWORK_EMPTY` (before the loading process starts or after that it has been aborted), `NETWORK_NO_SOURCE` (when no valid source has been found yet), `NETWORK_LOADING` (when the user agent is actually loading data), and `IDLE` (when the user agent decides to pause loading and then continue later).
+
+When "paused" in the `NO_SOURCE` state, loading can then resume as a reaction to changes in the children list of the media element.
+
+State transitions can trigger different events:
+- `loadstart`, when a valid source is found
+- `abort` and `emptied`, when the loading process is cancelled
+- `suspend`, when the user agent decides it already loaded enough content and wants to wait before loading more
+- `error`, when due to an error the source cannot continue loading
+
+While in the `LOADING` state two other events are fired multiple times, `progress` and `stalled`, indicating whether the server is currently sending data or not.
+![[network_states.svg]]
+
+#### The *ready* states
+They represent how much time of the video has been already loaded, potentially in relation to the current time position of the media element. They are exposed as the `.readyState` attribute of the media element.
+
+A video initially starts as `NOTHING`, and then as loading proceeded it transitions to `METADATA` (the video metadata has been loaded), `CURRENT_DATA` (there is enough content loaded to show one frame), `FUTURE_DATA` (there is enough content to play for a little bit) and `ENOUGH_DATA` (there is enough content that the user will probably be able to play the whole media element without buffering).
+
+As the time duration changes (either due to the media element playing, or due to the user seeking around) the ready state can transition *back* to `FUTURE_DATA` or even to `CURRENT_DATA`, potentially leading to the `canplay` and `canplaytrhough` events being later fired again. The `loadedmetadata` and `loadeddata` events are fired at most once, unless the element is fully reset by loading a different source.
+
+
+![[ready_states.svg]]
+#### The *playback* states
+They represent whether the video is currently playing or paused. These states are not actually exposed as a real enum-based state on the media element, but as separate attributes.
+
+These four states are `PLAYING`/`WAITING` and `PAUSED`/`ENDED`: each pair represents an *intended* state, but which of the two states the media element is actually in depends on the time position and the ready state.
+
+A video can become *potentially playing* by the user clicking the "Play" button, by the `.play()` JavaScript method, or by loading changes if the `autoplay` attribute of the media element is set. When that happens, the `play` event is fired, and then the video will oscillate between the `PLAYING` and `WAITING` states depending on whether there is enough content loaded to play or not. Whenever one of these two states is entered, a corresponding `playing` or `waiting` event is fired. While a video is `PLAYING`, it will fire multiple `timeupdate` events to mark the progress of time.
+
+A video can then be paused, either by the user clicking the "Pause" button, by the `.pause()` JavaScript method, by the media element reaching the end of its playback position with the `loop` attribute not set, or by a video that has been auto played not respecting the autoplay conditions anymore (for example, moving out of the viewport). When this happens, a `pause` event is fired.
+
+When a video reaches the end of the time duration and it's paused, an `ended` event will be fired: this can either happen naturally due to the video playing to its end, or due to seeking to the end.
+
+![[playback_states.svg]]
+#### Other actions and events
+
+A user agent can be required to *seek* at a new playback position , either through user action, by calling the `.fastSeek()` method, or by setting the `.currentTime` attribute. When this happens, the media element will fire a `seeking` event, a `timeupdate` event, and a `seeked` event. The playback state will continue as it was before seeking, potentially switching immediately after between `PLAYING` end `WAITING`, between `PAUSED` and `ENDED`, or from `PLAYING`/`WAITING` to `ENDED`.
+
+A media element's default playback rate and playback rate can change, either through user action or by setting the `.defaultPlaybackRate`/`.playbackRate` attributes. When this happens, a `ratechange` event is fired on the media element.
+
+Similarly, its volume can change, either through user action or by setting the `.volume` or `.muted` attributes. When this happens, a `volumechange` event is fired on the media element.
+#### AsyncContext propagation
+
+Media elements can expose controls that let the user interact with them, and they behave as if the corresponding JavaScript APIs were called. In those case, the "current AsyncContext" that gets captured is the *empty* context.
+
+Some the events fired on media elements are part of long-lived processes (e.g. loading, or playing), while others are due to an occasional action happening and completing.
+
+There are two possible approaches when it comes to the events fired as part of long-lived processes: one that involves sharing a single AsyncContext snapshot across all of them, and one that separates loading from playing.
+
+> [!NOTE]
+> **TODO:** We need to get more feedback from users and browser developers to pick one
+##### Option 1: Separate long-lived AsyncContext snapshots
+The media element has two slots for storing long-lived AsyncContext snapshots:
+- the one for *loading* events, used for:
+ - the events related to *network* states (`abort`, `emptied`, `loadstart`, `suspend`, `error`, `progress`, `stalled`)
+ - the events related to *ready* states (`loadedmetadata`, `loadeddata`, `canplay`, `canplaythrough`)
+ - the `resize` event fired on `video` elements after [fetching enough data to determine its dimensions](https://html.spec.whatwg.org/#getting-media-metadata), and the `durationchange` event fired once the length of the media resource [changed to a known value](https://html.spec.whatwg.org/#durationChange)
+- the one for *playing* events, used for:
+ - those related to `PLAYING`/`WAITING` *playback* states (`play`, `waiting`, `playing`, `timeupdate`)
+ - those related to the `ENDED` *playback* state, if it's reached due to the media resource playing all the way to the end (`pause`, `ended`)
+
+When running the [media element load algorithm](https://html.spec.whatwg.org/#media-element-load-algorithm) or the [resource selection algorithm](https://html.spec.whatwg.org/#concept-media-load-algorithm) due to any of the following:
+- changes to the `src`, `currentSrc`, or `srcObject` attributes (this includes creation through the [`new Audio(src)`](https://html.spec.whatwg.org/#dom-audio) constructor or [in other ways](concept-media-load-algorithm-at-creation))
+- calling the [`.load()`](https://html.spec.whatwg.org/#dom-media-load) method
+- [inserting a ``](https://html.spec.whatwg.org/#the-source-element:html-element-insertion-steps) as a child of the media element
+- [calling `.pause()` on a media element whose network status is `EMPTY`](https://html.spec.whatwg.org/#dom-media-pause)
+the user agent must synchronously capture the current AsyncContext and store it on the media element as the context for *loading* events.
+> [!NOTE]
+> **QUESTION:** Should the `emptied` and `abort` events fire in the new or old loading context?
+
+When running the [`.play()`](https://html.spec.whatwg.org/#dom-media-play) method of media elements, the user agent must synchronously capture the current AsyncContext and store in on the media element as the context for *playing* events.
+
+When running the [resource selection algorithm](https://html.spec.whatwg.org/#concept-media-load-algorithm) due to [playing a media element whose network status is `EMPTY`](https://html.spec.whatwg.org/#internal-play-steps), copy the new context for *playing* events as the context for *loading* events.
+
+When the *ready* state of an element eligible for autoplay [transitions to `ENOUGH_DATA`](https://html.spec.whatwg.org/#ready-states:dom-media-have_enough_data-2),
+- if the user agent decides to start playing it, it must copy the context for *loading* events as the context for *playing* events;
+- otherwise, if the user agent decides to only play the video once it enters the viewport, it must store the empty context as the context for *playing* events before doing so.
+
+> [!NOTE]
+> **tip:** - When setting a new context for *loading* events, the user agent can unobservably clear all the other contexts stored on the media element.
+> - When the media element gets paused, the user agent can unobservably clear the context for *playing* events.
+##### Option 2: Single long-lived AsyncContext snapshot
+On the media element there is a slot, that we can call *principal AsyncContext snapshot*, which stores an AsyncContext snapshot shared both by events related to *loading* and to *playing*. It behaves the same way as in the [Separate long-lived AsyncContext snapshots](#TODO: Link) section, but the context for *loading* events and the one for *playing* events live share the same slot, overwriting each other following a last-wins approach.
+##### Occasional actions
+Each occasional action stores its own AsyncContext snapshot on the media element, which can be cleared once the corresponding events are fired.
+
+When the `defaultPlaybackRate` or `playbackRate` attributes [change value](https://html.spec.whatwg.org/#rateUpdate), the user agent must:
+- synchronously capture the current AsyncContext (empty if the change is done through user interaction), and store it on the media element as the context for *ratechange* events
+- use that context to fire the corresponding `ratechange` event
+
+When the `volume` attribute [changes value](https://html.spec.whatwg.org/#set-the-playback-volume), or when [setting the muted state](https://html.spec.whatwg.org/#set-the-muted-state), the user agent must:
+- synchronously capture the current AsyncContext (empty if the change is done through user interaction), and store it on the media element as the context for *volumechange* events
+- use that context to fire the corresponding `volumechange` event
+
+When [pausing](https://html.spec.whatwg.org/#dom-media-pause), the user agent must:
+- if pausing [due to viewport changes](https://html.spec.whatwg.org/#ready-states:internal-pause-steps) or due to user interaction with the element, store the empty context on the media element as the context for *pausing* events
+- otherwise, if pausing due to the [media element being removed from the DOM tree](https://html.spec.whatwg.org/#playing-the-media-resource:internal-pause-steps-2) or because the `volume` attribute [changes value](https://html.spec.whatwg.org/#set-the-playback-volume), synchronously capture the current AsyncContext and store it on the media element as the context for *pausing* events
+- otherwise, if pausing due to the [playing the media resource to the end](https://html.spec.whatwg.org/#playing-the-media-resource:current-playback-position-4), copy the media element's context for *playing* events as the context for *pausing* events
+- the user agent must then use this "context for *pausing* events" for the corresponding `pause` event. If pausing due to the media element playing to the end, also use it for the `ended` event.
+
+> [!NOTE]
+> **INFO:** While the algorithm for pausing will fire a `timeupdate` event to notify the consumers of the final time reached by the media resource, that event will still be fired with the context for *playing* event, as it is conceptually the last event that happens as part of the playthrough process.
+
+When [seeking](https://html.spec.whatwg.org/#seeking), the user agent must:
+- if seeking due to any of the following reasons:
+ - the media element's [default playback start position](https://html.spec.whatwg.org/#loading-the-media-resource:dom-media-seek)
+ - an [initial position indicated by the resource/URL itself](https://html.spec.whatwg.org/#loading-the-media-resource:dom-media-seek-2)
+ - [changes in the earliest possible position](offsets-into-the-media-resource:earliest-possible-position-4)
+ - the duration [changing to a smaller value than the current playback position](https://html.spec.whatwg.org/#offsets-into-the-media-resource:dom-media-seek-3)
+ then copy the media element's context for *loading* events as the context for *seeking* events
+- otherwise, if seeking due to any of the following reasons:
+ - [reaching the end of a media resource](https://html.spec.whatwg.org/#playing-the-media-resource:current-playback-position-4) whose `loop` attribute is set
+ - running the [`play()`](https://html.spec.whatwg.org/#internal-play-steps) method on the media element whose playback has ended (after capturing the context for *playing* events)
+ then copy the media element's context for *playing* events as the context for *seeking* events
+- otherwise, if seeking due to any of the following reasons:
+ - [setting the `.currentTime` attribute](https://html.spec.whatwg.org/#offsets-into-the-media-resource:dom-media-seek) through JavaScript
+ - calling the [`.fastSeek()`](https://html.spec.whatwg.org/#dom-media-fastseek) method
+ then capture the current AsyncContext and store it on the media element as the context for *seeking* events
+- otherwise, if seeking due to any of the following reasons:
+ - the media resource is internally scripted or interactive
+ - the user seeks around through interaction with the media element
+ then store the empty context on the media element as the context for *seeking* events
+- the user agent must then use this "context for *seeking* events" to fire the corresponding `seeking`, `timeupdate`, and `seeked` events. If the media element was already paused before seeking and it seeks to the end, use this "context for *seeking* events" also to fire the corresponding `ended` event.
+
+A new seeking process can start while one is in progress: the new one will cancel any pending logic related to the first seek, and will overwrite the stored context for *seeking* events.
+
+Seeking to the end of a media resource that is potentially playing will effectively also fire `paused` and `ended` events. These events will be fired in the context for *playing* events, which is the same as what would happen if seeking just one nanosecond before the end and then letting the media resource play to completion.
+##### Events fired on other objects
+When the [resource selection algorithm](https://html.spec.whatwg.org/#concept-media-load-algorithm) running for a given media element fires an `error` event on one of its `` children, the user agent must use the empty context.
+
+When the [media data processing steps](https://html.spec.whatwg.org/#media-data-processing-steps-list) running for a given media element fire an `addtrack`, `removetrack`, or `change` event on the media element's `audioTracks` or `videoTracks` lists, the user agent must use the empty context.
+
+When any of the following:
+- the [steps to expose a media-resource-specific text track](html.spec.whatwg.org/#steps-to-expose-a-media-resource-specific-text-track) running for a given media element
+- the [steps when a `