---
title: Buffer
description: `Buffer` is the core wrapper around a `GPUBuffer`. Use it for explicit GPU buffer allocation through `Device.createBuffer(...)`, CPU-to-GPU writes, deterministic readback, and wrapper-aware teardown.
---

# Buffer



## Import

```ts
import { Buffer } from "vgpu/core";
```

## Signature

```ts
import type { Device } from "vgpu/core";

type BufferUsageName =
  | "map_read"
  | "map_write"
  | "copy_src"
  | "copy_dst"
  | "index"
  | "vertex"
  | "uniform"
  | "storage"
  | "indirect"
  | "query_resolve";

interface BufferOptions {
  readonly size: number;
  readonly usage: readonly BufferUsageName[];
  readonly label?: string;
}

type BufferWriteData = ArrayBuffer | ArrayBufferView<ArrayBuffer>;

declare class Buffer {
  readonly gpu: GPUBuffer;
  readonly options: BufferOptions;
  constructor(device: Device, gpu: GPUBuffer, options: BufferOptions);
  write(data: BufferWriteData, offset?: number): void;
  read(byteLength: number, offset?: number): Promise<ArrayBuffer>;
  destroy(): void;
  dispose(): void;
}
```

## Parameters

### `Device.createBuffer(opts)` / `BufferOptions`

| Param      | Type                         | Required | Default     | Notes                                                                              |
| ---------- | ---------------------------- | -------: | ----------- | ---------------------------------------------------------------------------------- |
| opts.size  | `number`                     |        ✔ | —           | Buffer byte length. Must be finite and greater than `0`.                           |
| opts.usage | `readonly BufferUsageName[]` |        ✔ | —           | One or more vgpu usage names mapped to `GPUBufferUsage` flags. Empty arrays throw. |
| opts.label | `string`                     |        ✖ | `undefined` | Forwarded to `GPUBufferDescriptor.label`.                                          |

Valid `BufferUsageName` values: `"map_read"`, `"map_write"`, `"copy_src"`, `"copy_dst"`, `"index"`, `"vertex"`, `"uniform"`, `"storage"`, `"indirect"`, `"query_resolve"`.

### Constructor

| Param   | Type            | Required | Default | Notes                                                                                                      |
| ------- | --------------- | -------: | ------- | ---------------------------------------------------------------------------------------------------------- |
| device  | `Device`        |        ✔ | —       | Owning device wrapper used for queue writes and readback. Normally supplied by `Device.createBuffer(...)`. |
| gpu     | `GPUBuffer`     |        ✔ | —       | Raw WebGPU buffer.                                                                                         |
| options | `BufferOptions` |        ✔ | —       | Original vgpu descriptor exposed as `buffer.options`.                                                      |

### `write(data, offset?)`

| Param  | Type                                          | Required | Default | Notes                                                                 |
| ------ | --------------------------------------------- | -------: | ------- | --------------------------------------------------------------------- |
| data   | `ArrayBuffer \| ArrayBufferView<ArrayBuffer>` |        ✔ | —       | Bytes passed to `device.queue.writeBuffer(buffer.gpu, offset, data)`. |
| offset | `number`                                      |        ✖ | `0`     | Destination byte offset in the GPU buffer.                            |

### `read(byteLength, offset?)`

| Param      | Type     | Required | Default | Notes                                                        |
| ---------- | -------- | -------: | ------- | ------------------------------------------------------------ |
| byteLength | `number` |        ✔ | —       | Number of bytes to copy from the GPU buffer into CPU memory. |
| offset     | `number` |        ✖ | `0`     | Source byte offset in the GPU buffer.                        |

**Returns:**

* `Device.createBuffer(opts)` returns `Buffer`.
* `write(data, offset?)`, `destroy()`, and `dispose()` return `void`.
* `read(byteLength, offset?)` returns `Promise<ArrayBuffer>` containing exactly the requested byte range.

**Throws:**

* `VGPU-CORE-INVALID-USAGE` from `Device.createBuffer(...)` when `opts.size` is non-finite, `opts.size <= 0`, or `opts.usage.length === 0` — pass a positive byte length and at least one usage.
* `VGPU-BUFFER-DISPOSED` (`"Buffer is destroyed."`) when `write(...)` or `read(...)` is called after `destroy()`/`dispose()` — create a new buffer instead of reusing a destroyed wrapper. Reported for a wrapper you disposed and for one destroyed with its gpu, and in preference to the owning device's own disposal error.
* Native WebGPU validation errors may occur if usage flags do not allow the operation, for example reading a buffer without `"copy_src"` or writing without `"copy_dst"`.

## Examples

```ts
import { createMockAdapter } from "vgpu/mock";

const device = await createMockAdapter().requestDevice();
const buffer = device.createBuffer({
  label: "cpu-visible-data",
  size: 16,
  usage: ["copy_dst", "copy_src"],
});

buffer.write(new Uint32Array([1, 2, 3, 4]));
const bytes = await buffer.read(16);
console.log(new Uint32Array(bytes)[2]); // 3

buffer.destroy();
device.destroy();
```

```ts
import { createMockAdapter } from "vgpu/mock";

const device = await createMockAdapter().requestDevice();

device.pushErrorScope("validation");
const placeholder = device.createBuffer({ size: 0, usage: ["copy_dst"] });
const error = await device.popErrorScope();

console.log(placeholder.options.size); // 0: wrapper exists, backing GPU buffer is mock-sized
console.log(error?.code); // "VGPU-CORE-INVALID-USAGE"

device.destroy();
```

## Notes

* `Buffer` does not infer usages. Include `"copy_dst"` for `write(...)`, `"copy_src"` for `read(...)`, `"vertex"` for vertex input, `"index"` for index input, `"uniform"` for uniforms, and `"storage"` for storage bindings.
* Prefer `buffer.destroy()`/`buffer.dispose()` over `buffer.gpu.destroy()` so lifecycle callbacks, mocks, and wrapper state stay synchronized.
* `destroy()` is idempotent. After destroy, `write(...)` and `read(...)` intentionally fail.
* `read(...)` copies through the device readback path; avoid it in frame loops unless you intentionally need CPU synchronization.
* **See also:** `Device`, `Queue`, `Texture`, `bind.resource`.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)