{"id":15369156,"url":"https://github.com/developit/element-worklet","last_synced_at":"2026-02-02T16:44:53.125Z","repository":{"id":66019980,"uuid":"330019621","full_name":"developit/element-worklet","owner":"developit","description":null,"archived":false,"fork":false,"pushed_at":"2021-01-15T22:27:36.000Z","size":9,"stargazers_count":31,"open_issues_count":3,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-24T04:18:36.596Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"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/developit.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"publiccode":null,"codemeta":null}},"created_at":"2021-01-15T20:51:26.000Z","updated_at":"2025-03-19T19:10:13.000Z","dependencies_parsed_at":"2023-03-10T23:29:27.558Z","dependency_job_id":null,"html_url":"https://github.com/developit/element-worklet","commit_stats":{"total_commits":3,"total_committers":1,"mean_commits":3.0,"dds":0.0,"last_synced_commit":"3b19aed7184a2b3ff4722859734ddba622f318fd"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Felement-worklet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Felement-worklet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Felement-worklet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Felement-worklet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/developit","download_url":"https://codeload.github.com/developit/element-worklet/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245931303,"owners_count":20695954,"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":[],"created_at":"2024-10-01T13:34:27.044Z","updated_at":"2026-02-02T16:44:48.106Z","avatar_url":"https://github.com/developit.png","language":null,"readme":"# Proposal: Element Worklet\n\n_Created:_ 2018-09-20 | _Updated:_ 2021-01-15\n\nThis proposal outlines a (potentially impossible!) idea for moving entire Elements off the main thread.\n\nConceptually, think [OOPIFs](https://www.chromium.org/developers/design-documents/oop-iframes) except well-suited to first-party content,\nand implemented as lightweight [Worklets](https://developer.mozilla.org/en-US/docs/Web/API/Worklet).\n\n## On the UI Thread:  Element Worklet\n\nThe Custom Elements API is extended with support for registering Element Worklets. As with all\nWorklets, registration is done via a URL resolving to a Module Script. A single Element Worklet\nscript can register multiple Custom Elements.\n\n```js\ncustomElements.addModule('./lazy-image.mjs');\n```\n\nAll Custom Elements controlled by a given Worklet are upgraded once the Worklet has been initialized,\nfollowing the same lifecycle as Custom Elements defined on the main thread.\n\nAside from the URL-based Worklet script registration and instantiation, Custom Elements defined using\nElement Worklet function like a standard Custom Element:\n\n```html\n\u003c!-- declarative: --\u003e\n\u003clazy-image src=\"cat.jpeg\"\u003e\n\n\u003cscript\u003e\n  // imperative:\n  const dog = document.createElement('lazy-image');\n  dog.setAttribute('src', 'dog.png');\n  document.body.appendChild(dog);\n\u003c/script\u003e\n```\n\n### Pros \u0026 Cons\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003e\u003cstrong\u003ePros\u003c/strong\u003e\u003c/th\u003e\n\u003cth\u003e\u003cstrong\u003eCons\u003c/strong\u003e\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cul\u003e\n  \u003cli\u003eRe-uses existing Element registry\u003c/li\u003e\n  \u003cli\u003eLeverages Custom Element semantics\u003c/li\u003e\n  \u003cli\u003eWell-defined lifecycle and upgrade model\u003c/li\u003e\n\u003c/ul\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cul\u003e\n  \u003cli\u003eLimited DOM API surface\u003c/li\u003e\n  \u003cli\u003eNo declarative registration **\u003c/li\u003e\n\u003c/ul\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003e \\*\\* However, Element Worklet lends itself more to declarative registration than straight Custom Elements.\n\n## Inside an Element Worklet\n\nElement Worklets build on the same foundation as the other existing types of Worklets.\nThe module URL given to `customElements.addModule()` is immediately fetched and evaluated within a new\n`ElementWorkletGlobalScope`. This may occur on the rendering thread, in a background thread or a thread pool.\n\n```js\n// lazy-image.mjs\nclass LazyImageElement extends WorkletElement {\n  constructor() {\n    // Light DOM is accessible to the worklet:\n    this.getAttribute('src')  // cat.jpeg\n\n    // ...but it is read-only\n    this.firstElementChild.src = 'foo'  // Error\n\n    // and scoped to the element (no parent references)\n    this.parentNode  // null\n\n    // in fact, there are no meaningful instance properties defined on WorkletElements at all.\n    // to facilitate serialization, all data must flow through attributes or MessageChannel:\n\n    // there's a corresponding `lazyImage.port` on the main thread representation.\n    this.port.postMessage('hello');\n\n    // As such, Shadow DOM is required in order to produce UI:\n    this.shadow = this.createShadow();\n    this.shadow.innerHTML = 'some stuff';\n  }\n\n  // all lifecycle functions the same as Custom Elements v1:\n\n  static get observedAttributes() {\n    return ['src']\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) {}\n  connectedCallback() {}\n  disconnectedCallback() {}\n}\n\n// Registering an Element is the same as on the main thread:\ncustomElements.define('lazy-image', LazyImageElement);\n```\n\n**Data Flow:** Custom Element properties are not reflected on a WorkletElement, only attributes.\nAttribute changes are observed the same as they are in Custom Elements, defined eagery by a static `observedAttributes` property on the element's constructor.\nAttribute changes changes invoke `attributeChangedCallback()` in the worklet, and may be batched.\n\n**Data Sharing:** The main thread and worklet instances of a Custom Element each have a `.port` property, which are ports of an instance-specific `MessageChannel` that can be used for message passing. This is synonymous with the `processor.port` interface used by AudioWorkletNode/AudioWorkletProcessor.\n\n## Element Upgrade Process\n\nWhen a Custom Element defined by an ElementWorklet is instantiated on the main thread,\na new corresponding WorkletElement instance is created within its associated `ElementWorkletGlobalScope`:\n\n1. all unknown elements begin marked as unresolved (`HTMLUnknownElement` / `:not(:resolved)`)\n2. `customElements.addModule()` is invoked and an ElementWorklet instantiated\n3. `customElements.define()` is invoked within the Worklet\n4. Elements in the document with the given Custom Element name are enqueued for upgrade\n\nUpgrading Worklet-backed Elements is a two-phase process. First, the Element representation is\ncreated on the main thread and a MessagePort is created for it, with one port assigned as a `port`\ninstance property. At this point, the Element is still considered to be in an unresolved state.\nThen, inside the Worklet, a corresponding WorkletElement is instantiated and assigned the other\nmessage port.\n\nWhen a Worklet-backed Element is connected (via `appendChild()`, the parser or another means),\nthe WorkletElement instance's `connectedCallback()` lifecycle method is invoked.\nAs with a standard Custom Element, `attributeChangedCallback()` is also invoked with the initial value of each observed attribute.\nOnce all the callbacks have been invoked, the Element's representation in the parent document is marked as resolved and the process is complete.\n\n## Use Cases\n\n**Ads:** Advertisements currently use iframes for encapsulation, a technique of increasing cost as\nthe effects of Spectre mitigations make their way into browsers.  Element Worklet could provide a\nlightweight alternative to iframes for this use-case.\n\n**Third Party Embeds:**  Embedded content like comment widgets, chats and helpdesks all of these\ncurrently use some combination of same-origin scripting and iframes, usually mixing origins\n(eg: a script in the embedder context communicating with an iframe from the embedee's context).\nMoving from `\u003cscript\u003e` + `\u003ciframe\u003e` to Element Worklet seems like a reasonable fit for this case.\n\n**AMP:** The semantics defined in this proposal map reasonably well to `\u003camp-script\u003e`, and a prototype\nof Element Worklet has been built using `worker-dom`, the library that underpins `\u003camp-script\u003e`.\nAMP's approach is much more broadly applicable than Element Worklet, seeking to support arbitrary\nthird-party code running in a sandboxed DOM environment. However, it's possible a solution like worker-dom\nwould be able to leverage something like Element Worklet to simplify Element registration and upgrades,\nand to mitigate transfer overhead between threads.\n\n**Lazy Loading:** Component-based frameworks and libraries strive to provide solutions for lazily\ndownloading, instantiating and rendering portions of an application. This process is entirely\nimplemented in userland, which has the unfortunate side effect of making it invisible to the browser.\nIn certain scenarios, it may be possible to use Element Worklet as the underly mechanism for lazily\nloading and rendering pieces of a component-based User Interface.\n\n**UI Component Libraries:** If this model can be shown to provide performance guarantees for Element\nregistration, upgrade and rendering, it's possible a UI library would choose to provide their components\nas worklet-backed Elements through the use of one or more Element Worklets. This could have interesting\nimplications for performance, since it would provide a way to impose performance guarantees. This is a\nsafety net developers do not currently have for prebuilt modules. The (large) portions of a typical app\nthat are defined by code installed from npm would have less ability to negatively impact the performance\nof first-party code.\n\n## Open Questions\n\n- What DOM APIs are available from within an ElementWorkletGlobalScope?\n- Can `WorkletElement` provide a sufficient API surface to allow current libraries and approaches to be\n  reused with minimal modification?\n- Is the level of encapsulation illustrated in this proposal too limiting?  Does it fail to meet the\n  needs of use-cases like embedded video players?\n- Should Element Worklet provide an analog for Custom Element property getters/setters? Could custom\n  properties (and/or methods?) defined on a WorkletElement subclass be reflected asynchronously to the\n  main thread (in the style of [Comlink](https://github.com/GoogleChromeLabs/comlink)? Without this,\n  complex data types for Worklet-backed elements would need to be serialized as attributes.\n- Would it be possible to allow defining a priority when registering Element Worklet modules?\n  This would unlock a host of use-cases in which worklet code could be considered untrusted.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevelopit%2Felement-worklet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevelopit%2Felement-worklet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevelopit%2Felement-worklet/lists"}