Skip to Content
💡The WebNN Origin Trial is coming soon! Developers will soon be able to sign up for trial keys to explore →

MLContext

Experimental: This is an experimental technology. Check the browser compatibility table carefully before using it in production.

The MLContext interface of the Web Neural Network (WebNN) API represents the global state of neural network compute workloads and their execution. It owns the resources used to compile and run MLGraph objects, creates and reads/writes MLTensor objects, and reports implementation-specific operator support.

An MLContext is created with ML.createContext(). Each context has a context type:

Context typeCreated byDescription
"default"createContext(options)The user agent selects the compute device according to the MLContextOptions hints.
"webgpu"createContext(gpuDevice)The context is created from a WebGPU GPUDevice.

All work that touches tensor data — graph execution, tensor writes and reads — is queued in order on the context’s timeline, so calls such as writeTensor(), dispatch() and readTensor() can be issued back-to-back without awaiting each one.

This feature is available only in secure contexts  (HTTPS or localhost), and is available in both the main thread and Web Workers .

Web IDL

web-idl
typedef record<USVString, MLTensor> MLNamedTensors; dictionary MLContextLostInfo { DOMString message; }; [SecureContext, Exposed=(Window, Worker)] interface MLContext { undefined dispatch(MLGraph graph, MLNamedTensors inputs, MLNamedTensors outputs); Promise<MLTensor> createTensor(MLTensorDescriptor descriptor); Promise<MLTensor> createExportableTensor( MLTensorDescriptor descriptor, GPUDevice gpuDevice); Promise<MLTensor> createConstantTensor( MLOperandDescriptor descriptor, AllowSharedBufferSource inputData); Promise<ArrayBuffer> readTensor(MLTensor tensor); Promise<undefined> readTensor(MLTensor tensor, AllowSharedBufferSource outputData); undefined writeTensor(MLTensor tensor, AllowSharedBufferSource inputData); GPUBuffer exportToGPU(MLTensor tensor); MLOpSupportLimits opSupportLimits(); undefined destroy(); readonly attribute boolean accelerated; readonly attribute Promise<MLContextLostInfo> lost; };

Instance properties

accelerated

A read-only boolean indicating whether the context uses massively parallel acceleration (such as a GPU or NPU) rather than CPU-only processing. It reflects the accelerated option the context was created with (true by default, and always true for "webgpu" contexts).

accelerated.js
const context = await navigator.ml.createContext({ powerPreference: 'low-power' }); if (!context.accelerated) { console.log('Running on CPU; consider a smaller model.'); }

lost

A read-only Promise that fulfills with an MLContextLostInfo object when the context is lost, i.e. when its underlying execution device is no longer available or destroy() is called. The promise never rejects.

When a context is lost, every MLGraph and MLTensor created from it is destroyed, pending operations fail, and new operations on it are rejected with an InvalidStateError. To recover, create a new context and rebuild your graphs.

lost.js
context.lost.then((info) => { console.warn('MLContext lost:', info.message); // Recreate the context, rebuild graphs, and reallocate tensors here. });

Instance methods

MethodDescription
dispatch()Schedules execution of a compiled graph with the given input and output tensors.
createTensor()Creates an MLTensor whose contents are initialized to zero.
createExportableTensor()Creates an MLTensor that can be exported to a WebGPU GPUDevice.
createConstantTensor()Creates an immutable constant MLTensor initialized with the given data.
readTensor()Reads the contents of a tensor back to script.
writeTensor()Writes data into a tensor.
exportToGPU()Exports a tensor to its associated GPUDevice as a GPUBuffer.
opSupportLimits()Returns the data types and ranks supported by this context for each operator.
destroy()Releases all resources associated with the context.

dispatch()

The dispatch() method schedules the computational workload of a compiled MLGraph on the context’s timeline.

dispatch() returns immediately and provides no signal that execution has completed. To get results, call readTensor() on an output tensor and await it; reads are ordered after previously dispatched work.

Syntax

syntax.js
dispatch(graph, inputs, outputs)

Parameters

  • graph

    The MLGraph to execute. It must have been built from a builder associated with this context.

  • inputs

    An object (MLNamedTensors) mapping each graph input name to an MLTensor. It must contain exactly one tensor per graph input, and each tensor’s dataType and shape must match the corresponding input operand.

  • outputs

    An object (MLNamedTensors) mapping each graph output name to an MLTensor that receives the result. It must contain exactly one tensor per graph output, with matching dataType and shape.

Return value

None (undefined).

Exceptions

  • TypeError

    Thrown if the graph or any tensor belongs to a different context; if a tensor has been destroyed, is currently exported, or is a constant tensor; if the same tensor appears more than once across inputs and outputs; or if the named tensors do not exactly match the graph’s inputs or outputs.

  • InvalidStateError DOMException

    Thrown if the graph has been destroyed.

createTensor()

The createTensor() method creates an MLTensor associated with this context. All bytes of the new tensor are initialized to zero.

Syntax

syntax.js
createTensor(descriptor)

Parameters

  • descriptor

    An MLTensorDescriptor specifying the tensor’s dataType and shape, and whether it is readable and/or writable from script.

Return value

A Promise that fulfills with a new MLTensor.

Exceptions

The returned promise is rejected with:

  • InvalidStateError DOMException

    The context is lost, or was lost before the tensor was allocated.

  • UnknownError DOMException

    The tensor memory could not be allocated.

createExportableTensor()

The createExportableTensor() method creates an MLTensor that may later be exported to the given WebGPU GPUDevice with exportToGPU(). Its contents are initialized to zero.

Whether a context supports exporting to a particular GPUDevice is implementation-defined and may depend on the context type and platform.

Syntax

syntax.js
createExportableTensor(descriptor, gpuDevice)

Parameters

  • descriptor

    An MLTensorDescriptor.

  • gpuDevice

    The GPUDevice to which the tensor may be exported.

Return value

A Promise that fulfills with a new exportable MLTensor.

Exceptions

The returned promise is rejected with:

  • InvalidStateError DOMException

    The context is lost, or gpuDevice is lost.

  • NotSupportedError DOMException

    The context cannot export tensors to gpuDevice.

  • UnknownError DOMException

    The tensor memory could not be allocated.

createConstantTensor()

The createConstantTensor() method creates a constant MLTensor initialized with a copy of inputData. A constant tensor’s constant property is true; it is neither readable nor writable, and cannot be bound as a dispatch() input or output.

Constant tensors are intended to be passed to MLGraphBuilder.constant(tensor), so that large weights are uploaded to the device once and can be shared by several graphs. It is legal to destroy a constant tensor once the graphs that use it have been built.

Syntax

syntax.js
createConstantTensor(descriptor, inputData)

Parameters

  • descriptor

    An MLOperandDescriptor specifying the dataType and shape.

  • inputData

    An ArrayBuffer, SharedArrayBuffer, or ArrayBufferView whose bytes are copied into the tensor. See Buffer requirements.

Return value

A Promise that fulfills with a new constant MLTensor.

Exceptions

The returned promise is rejected with:

  • TypeError

    The descriptor has an invalid shape, or inputData does not satisfy the buffer requirements.

  • InvalidStateError DOMException

    The context is lost.

  • UnknownError DOMException

    The tensor could not be allocated or initialized.

readTensor()

The readTensor() method reads the contents of an MLTensor from the context’s timeline back to script. The read happens after all previously queued work on the context, so awaiting it is the way to wait for a dispatch() to finish.

There are two overloads: one that allocates and returns a new ArrayBuffer, and a bring-your-own-buffer (BYOB) variant that writes into a buffer you provide.

Syntax

syntax.js
readTensor(tensor) readTensor(tensor, outputData)

Parameters

  • tensor

    The MLTensor to read. It must have been created with readable: true.

  • outputData optional

    An ArrayBuffer, SharedArrayBuffer, or ArrayBufferView that receives the data. See Buffer requirements.

Return value

  • readTensor(tensor): a Promise that fulfills with a new ArrayBuffer containing a copy of the tensor data.
  • readTensor(tensor, outputData): a Promise that fulfills with undefined once the data has been written into outputData.

Exceptions

The returned promise is rejected with:

  • TypeError

    The tensor belongs to a different context, has been destroyed, is not readable, or is currently exported; or outputData does not satisfy the buffer requirements or was detached before the read completed.

  • InvalidStateError DOMException

    The context was lost, or the tensor was destroyed while the read was pending.

  • UnknownError DOMException

    The data could not be read.

writeTensor()

The writeTensor() method copies the bytes of inputData into an MLTensor on the context’s timeline. The data is copied synchronously when the method is called, so inputData can be reused or modified immediately afterwards.

Like dispatch(), writeTensor() gives no signal when the write has completed; subsequent dispatch() and readTensor() calls are ordered after it.

Syntax

syntax.js
writeTensor(tensor, inputData)

Parameters

  • tensor

    The MLTensor to write to. It must have been created with writable: true.

  • inputData

    An ArrayBuffer, SharedArrayBuffer, or ArrayBufferView containing the data. See Buffer requirements.

Return value

None (undefined).

Exceptions

  • TypeError

    Thrown if the tensor belongs to a different context, has been destroyed, is not writable, or is currently exported, or if inputData does not satisfy the buffer requirements.

exportToGPU()

The exportToGPU() method exports an exportable MLTensor (created with createExportableTensor()) to its associated GPUDevice and returns a GPUBuffer that WebGPU commands can use.

While a tensor is exported, the GPUDevice has exclusive access to its contents, and dispatch(), readTensor() and writeTensor() calls that use it throw (or reject with) a TypeError. Calling GPUBuffer.destroy() on the returned buffer returns the tensor to WebNN, after which it can be used again.

  • The returned buffer has a size equal to the tensor’s byte length and a usage of GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST.
  • Calling exportToGPU() again on a tensor that is already exported returns the same GPUBuffer.
  • The user agent orders the transfer correctly: WebGPU work submitted using the buffer runs after pending WebNN work on the tensor, and WebNN work after destroy() runs after pending WebGPU work on the buffer. No manual synchronization is required.
  • Exporting is not guaranteed to be zero-copy; the contents may be copied to and from GPU memory.
  • Destroying the tensor or its context while exported does not invalidate the GPUBuffer. If the GPUDevice is lost, the tensor stays exported until GPUBuffer.destroy() is called.

Syntax

syntax.js
exportToGPU(tensor)

Parameters

  • tensor

    An exportable MLTensor created by this context.

Return value

A GPUBuffer associated with the tensor’s GPUDevice.

Exceptions

  • TypeError

    Thrown if the tensor belongs to a different context, has been destroyed, or was not created with createExportableTensor().

  • InvalidStateError DOMException

    Thrown if the tensor’s GPUDevice is lost.

opSupportLimits()

The opSupportLimits() method returns an MLOpSupportLimits dictionary describing which data types and tensor ranks this context supports, both globally and for each operator. Use it to choose a model variant (for example float16 vs. float32, or "nchw" vs. "nhwc") that runs best on the current platform.

Every implementation supports at least the required data types and ranks listed for each operator in MLGraphBuilder operations, and may support more.

opSupportLimits() is not intended to add fingerprinting entropy; in current implementations the same information can be inferred from the OS and browser version.

Syntax

syntax.js
opSupportLimits()

Parameters

None.

Return value

An MLOpSupportLimits dictionary.

destroy()

The destroy() method releases all resources associated with the context. It loses the context: the lost promise fulfills with an implementation-defined message, and all graphs and tensors created from the context are destroyed. Outstanding compute requests and tensor creation, read, and write requests fail. Calling destroy() on a context that is already lost does nothing.

Syntax

syntax.js
destroy()

Return value

None (undefined).

Buffer requirements

createConstantTensor(), readTensor(tensor, outputData) and writeTensor() (as well as MLGraphBuilder.constant()) validate the buffer against the tensor descriptor:

  1. The buffer’s byte length must equal the descriptor’s byte length (number of elements × element size).
  2. An ArrayBuffer or SharedArrayBuffer is always accepted.
  3. An ArrayBufferView is accepted if it is a Uint8Array (a generic byte view, e.g. a slice of a WebAssembly.Memory), or if its type matches the data type:
MLOperandDataTypeMatching ArrayBufferViewElement size
"float32"Float32Array4 bytes
"float16"Float16Array2 bytes
"int64"BigInt64Array8 bytes
"uint64"BigUint64Array8 bytes
"int32"Int32Array4 bytes
"uint32"Uint32Array4 bytes
"int8"Int8Array1 byte
"uint8"Uint8Array1 byte

Where Float16Array is not available yet, pass the raw IEEE 754 half-precision bits through a Uint16Array’s underlying ArrayBuffer (or a Uint8Array view).

Dictionaries

MLContextLostInfo

The value that the lost promise fulfills with.

MemberTypeDescription
messageDOMStringAn implementation-defined message describing why the context was lost.

MLOpSupportLimits

Returned by opSupportLimits(). Besides the top-level members below, it has one member per operator, named after the MLGraphBuilder method (for example conv2d, add, softmax), whose value describes the limits for that operator’s operands.

web-idl
dictionary MLOpSupportLimits { MLInputOperandLayout preferredInputLayout; [EnforceRange] unsigned long long maxTensorByteLength; MLTensorLimits input; MLTensorLimits constant; MLTensorLimits output; // ...plus one member per operator, e.g.: // MLConv2dSupportLimits conv2d; // MLBinarySupportLimits add; // MLSingleInputSupportLimits softmax; };
MemberTypeDescription
preferredInputLayoutMLInputOperandLayoutThe preferred input layout ("nchw" or "nhwc") for layout-dependent operators such as conv2d().
maxTensorByteLengthunsigned long longThe maximum supported tensor size, in bytes.
inputMLTensorLimitsLimits for graph input operands.
constantMLTensorLimitsLimits for graph constant operands.
outputMLTensorLimitsLimits for graph output operands.
operator nameoperator-specific dictionaryLimits for each operand of that operator, for example MLSingleInputSupportLimits or MLBinarySupportLimits. See each operator in MLGraphBuilder operations.

MLTensorLimits

web-idl
typedef sequence<MLOperandDataType> MLDataTypeList; dictionary MLTensorLimits { MLDataTypeList dataTypes; MLRankRange rankRange; };
MemberTypeDescription
dataTypessequence<MLOperandDataType>The supported data types.
rankRangeMLRankRangeThe minimum and maximum supported ranks.

MLRankRange

web-idl
dictionary MLRankRange { unsigned long min; unsigned long max; };
MemberTypeDescription
minunsigned longThe minimum supported rank.
maxunsigned longThe maximum supported rank.

MLSingleInputSupportLimits

Used by operators with one tensor input, such as relu(), softmax() or reshape().

web-idl
dictionary MLSingleInputSupportLimits { MLTensorLimits input; MLTensorLimits output; };

MLBinarySupportLimits

Used by operators with two tensor inputs a and b, such as add(), equal() or matmul().

web-idl
dictionary MLBinarySupportLimits { MLTensorLimits a; MLTensorLimits b; MLTensorLimits output; };

Other operators use dedicated dictionaries with one MLTensorLimits member per operand — for example MLConv2dSupportLimits has input, filter, bias and output. They are listed with each operator in MLGraphBuilder operations.

Examples

Executing a graph with MLTensors

dispatch.js
const descriptor = { dataType: 'float32', shape: [2, 2] }; const context = await navigator.ml.createContext(); const builder = new MLGraphBuilder(context); // 1. Create a computational graph 'C = 0.2 * A + B'. const constant = builder.constant(descriptor, new Float32Array(4).fill(0.2)); const A = builder.input('A', descriptor); const B = builder.input('B', descriptor); const C = builder.add(builder.mul(A, constant), B); // 2. Compile the graph. const graph = await builder.build({ C }); // 3. Create reusable input and output tensors. const [inputTensorA, inputTensorB, outputTensorC] = await Promise.all([ context.createTensor({ dataType: A.dataType, shape: A.shape, writable: true }), context.createTensor({ dataType: B.dataType, shape: B.shape, writable: true }), context.createTensor({ dataType: C.dataType, shape: C.shape, readable: true }), ]); // 4. Initialize the inputs. context.writeTensor(inputTensorA, new Float32Array(4).fill(1.0)); context.writeTensor(inputTensorB, new Float32Array(4).fill(0.8)); // 5. Execute the graph. context.dispatch(graph, { A: inputTensorA, B: inputTensorB }, { C: outputTensorC }); // 6. Read back the computed result. const result = await context.readTensor(outputTensorC); console.log('Output value:', new Float32Array(result)); // [1, 1, 1, 1]

Reading into an existing buffer

Reusing an output buffer avoids allocating a new ArrayBuffer for every inference:

read-byob.js
const output = new Float32Array(4); for (const frame of frames) { context.writeTensor(inputTensor, frame); context.dispatch(graph, { input: inputTensor }, { output: outputTensor }); await context.readTensor(outputTensor, output); render(output); }

Sharing weights with a constant tensor

constant-tensor.js
const weightDesc = { dataType: 'float32', shape: [4, 4] }; const weights = await context.createConstantTensor(weightDesc, new Float32Array(16).fill(0.5)); console.log(weights.constant); // true async function buildGraph() { const builder = new MLGraphBuilder(context); const x = builder.input('x', { dataType: 'float32', shape: [1, 4] }); return builder.build({ y: builder.matmul(x, builder.constant(weights)) }); } // Both graphs reuse the same device-side weights. const [graphA, graphB] = await Promise.all([buildGraph(), buildGraph()]); weights.destroy(); // Safe once the graphs are built.

Checking operator support limits

op-support-limits.js
const context = await navigator.ml.createContext(); const limits = context.opSupportLimits(); console.log('Preferred layout:', limits.preferredInputLayout); // "nchw" or "nhwc" console.log('Max tensor size:', limits.maxTensorByteLength, 'bytes'); // Pick a float16 model if conv2d supports float16 inputs on this context. const useFp16 = limits.conv2d.input.dataTypes.includes('float16'); const modelUrl = useFp16 ? 'model-fp16.onnx' : 'model-fp32.onnx'; // Check whether gather() accepts int64 indices. const supportsInt64Indices = limits.gather.indices.dataTypes.includes('int64'); // Check the maximum rank supported by add(). console.log('add() max rank:', limits.add.a.rankRange.max);

Interoperating with WebGPU

webgpu-interop.js
// 1. Create WebGPU and WebNN contexts. const gpuAdapter = await navigator.gpu.requestAdapter(); const gpuDevice = await gpuAdapter.requestDevice(); const context = await navigator.ml.createContext(); // 2. Build a simple graph: y = x + 1. const builder = new MLGraphBuilder(context); const descriptor = { dataType: 'float32', shape: [4] }; const x = builder.input('x', descriptor); const one = builder.constant(descriptor, new Float32Array([1, 1, 1, 1])); const graph = await builder.build({ y: builder.add(x, one) }); // 3. Create exportable tensors and run WebNN dispatch. const inputTensor = await context.createExportableTensor( { ...descriptor, writable: true }, gpuDevice); const outputTensor = await context.createExportableTensor( { ...descriptor, readable: true }, gpuDevice); context.writeTensor(inputTensor, new Float32Array([1, 2, 3, 4])); context.dispatch(graph, { x: inputTensor }, { y: outputTensor }); // 4. Export to WebGPU and use the buffer in GPU commands. const gpuBuffer = context.exportToGPU(outputTensor); // ... use gpuBuffer in WebGPU compute or render passes ... // 5. Return the tensor to WebNN, then read it from WebNN again. gpuBuffer.destroy(); const result = new Float32Array(await context.readTensor(outputTensor)); console.log(result); // [2, 3, 4, 5]

Specifications

SpecificationStatus
Web Neural Network API — MLContext interfaceCandidate Recommendation Draft

The latest Editor’s Draft is available at webmachinelearning.github.io/webnn .

Browser compatibility

WebNN is under active development and support varies across browsers, operating systems, and hardware. createExportableTensor() and exportToGPU() are recent additions and may not be implemented yet.

See Browser Compatibility: WebNN APIs.

See also

Last updated on