Skip to main content
Scripts run in a Web Worker-shaped environment: standard JavaScript plus the browser APIs that make sense without a document. There is self, there is no window and no document, so libraries that feature-detect their way into a browser build land on the right path without then reaching for a DOM. Everything on this page is global - no import needed. These are the specs’ APIs, so MDN is the reference for the details; this page is about what exists here and where it differs.
HTTP and sockets have a page of their own: Networking - fetch, WebSocket, EventSource, XMLHttpRequest.

Events

Full WHATWG event system, the same objects a browser gives you.
Event, CustomEvent, UIEvent, MouseEvent, WheelEvent, KeyboardEvent, ErrorEvent, MessageEvent, PromiseRejectionEvent, EventTarget. addEventListener supports the usual options - { once, capture, passive, signal } - and preventDefault() / stopImmediatePropagation() behave as specified. You can build your own emitters:
See Globals for the list of events the host fires.

AbortController

AbortSignal.timeout(ms), AbortSignal.any([...]), signal.throwIfAborted() and signal.reason all work. Aborting rejects with a DOMException named AbortError, exactly like in a browser.

URL & URLSearchParams

Relative resolution, credentials, ports, dot segments and query editing are all supported. There is no IDNA/punycode conversion for non-ASCII hostnames.

Encoding

TextEncoder is UTF-8 only, as specified, and has encodeInto. TextDecoder handles a small set of encodings - enough for text that is not UTF-8 without pretending to be a full ICU: Anything else throws a RangeError rather than quietly returning the wrong text. fatal and ignoreBOM are supported, and so is streaming - a code point split across two chunks survives it:

Streams

ReadableStream, WritableStream, TransformStream, both queuing strategies, TextEncoderStream / TextDecoderStream, and BYOB readers. This is what response bodies are, and what streaming parsers expect to find.
tee(), pipeTo, pipeThrough, async iteration and getReader({ mode: "byob" }) all work. A BYOB read copies into the buffer you hand it rather than filling it in place, which costs one copy and is otherwise invisible.

Compression

Both are TransformStreams, so they slot into any pipeline. Brotli is decode-only - the spec asks for no encoder, and nothing here needs one.

Blob, File & FileReader

FileReader is here too - readAsText (with a charset from the blob’s own type, or one you pass), readAsArrayBuffer, readAsDataURL, readAsBinaryString - with the event order older upload code waits for. New code should just await blob.text().

structuredClone

Deep copy with cycle support for objects, arrays, Map, Set, Date, RegExp, ArrayBuffer, typed arrays and errors. Functions throw a DataCloneError, and class instances lose their prototype - same as a browser.

crypto

crypto.subtle

The full SubtleCrypto surface - digest, generateKey, importKey, exportKey, sign, verify, encrypt, decrypt, deriveBits, deriveKey, wrapKey, unwrapKey - so jose and friends sign and verify tokens for real.
Not there: Ed25519, X25519, SHA-3, and AES-KW as an algorithm of its own (wrapKey/unwrapKey go through the cipher path instead).
Unlike a request, crypto.subtle work happens on the script thread. It is invisible for hashes, HMAC and AES, but generateKey on RSA costs tens of milliseconds and RSA-4096 far more - long enough to see. Generate a key once and keep it, rather than per call.

Timing & scheduling

requestAnimationFrame is driven by the same frame as the render event, so a callback scheduled from it can draw.

MessageChannel

Messages are structured-cloned and delivered asynchronously. Useful on its own, and it is what schedulers inside some libraries expect to find. userAgent, platform, language, languages, hardwareConcurrency, onLine. Enough for libraries that sniff the environment; nothing behind it is real hardware detection.

Not available

Reaching for any of these will throw a ReferenceError - the honest list saves an hour of debugging: WebAssembly is available - see WASM - which is the practical escape hatch when JS is not fast enough.
Not sure whether something exists in the build you are running? Ask it directly: