---
title: createRenderBundle
description: Low-level render bundle helper around `GPUDevice.createRenderBundleEncoder`. Prefer main API (`vgpu`) `bundle(gpu, { target }, cb)` when recording main API (`vgpu`) `Draw`/`Effect` commands because it derives formats from `Target` and performs stale-bundle checks (`VGPU-R3-BUNDLE-STALE`).
---

# createRenderBundle



## Import

```ts
import { createRenderBundle, RenderBundleRecorder } from "vgpu/core";
import type { RenderBundleOptions } from "vgpu/core";
```

## Signature

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

declare interface RenderPassDrawOptions {
  readonly vertexCount: number;
  readonly instanceCount?: number;
  readonly firstVertex?: number;
  readonly firstInstance?: number;
}

type RenderPassDynamicOffsets = readonly GPUBufferDynamicOffset[] | Uint32Array;

declare interface RenderBundleOptions {
  readonly label?: string;
  readonly colorFormats: readonly (GPUTextureFormat | null)[];
  readonly depthStencilFormat?: GPUTextureFormat;
  readonly sampleCount?: number;
  readonly depthReadOnly?: boolean;
  readonly stencilReadOnly?: boolean;
  readonly record: (bundle: RenderBundleRecorder) => void;
}

declare class RenderBundleRecorder {
  readonly gpu: GPURenderBundleEncoder;
  constructor(gpu: GPURenderBundleEncoder);
  setPipeline(pipeline: GPURenderPipeline): void;
  setBindGroup(index: number, group: GPUBindGroup | null, dynamicOffsets?: RenderPassDynamicOffsets): void;
  setVertexBuffer(slot: number, buffer: Buffer | GPUBuffer | null, offset?: number, size?: GPUSize64): void;
  draw(options: RenderPassDrawOptions): void;
  draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void;
}

declare function createRenderBundle(device: { readonly gpu: GPUDevice }, opts: RenderBundleOptions): GPURenderBundle;
```

## Parameters

| Param                                | Type                                               | Required | Default                      | Notes                                                                               |
| ------------------------------------ | -------------------------------------------------- | -------: | ---------------------------- | ----------------------------------------------------------------------------------- |
| device                               | `{ readonly gpu: GPUDevice }`                      |        ✔ | —                            | Core `Device` or any wrapper exposing a native `GPUDevice` as `.gpu`.               |
| opts                                 | `RenderBundleOptions`                              |        ✔ | —                            | Native render bundle encoder options plus callback.                                 |
| opts.label                           | `string`                                           |        ✖ | `undefined`                  | Passed to `createRenderBundleEncoder` and `finish`.                                 |
| opts.colorFormats                    | `readonly (GPUTextureFormat \| null)[]`            |        ✔ | —                            | Must match the render pass where the bundle will execute.                           |
| opts.depthStencilFormat              | `GPUTextureFormat`                                 |        ✖ | `undefined`                  | Required when the replay pass has depth/stencil.                                    |
| opts.sampleCount                     | `number`                                           |        ✖ | WebGPU encoder default (`1`) | Must match replay pass sample count. main API (`vgpu`) passes `target.sampleCount`. |
| opts.depthReadOnly                   | `boolean`                                          |        ✖ | `undefined`                  | Forwarded to WebGPU encoder descriptor.                                             |
| opts.stencilReadOnly                 | `boolean`                                          |        ✖ | `undefined`                  | Forwarded to WebGPU encoder descriptor.                                             |
| opts.record                          | `(bundle: RenderBundleRecorder) => void`           |        ✔ | —                            | Called synchronously before `encoder.finish()`.                                     |
| recorder.setPipeline.pipeline        | `GPURenderPipeline`                                |        ✔ | —                            | Native pipeline compatible with bundle formats.                                     |
| recorder.setBindGroup.index          | `number`                                           |        ✔ | —                            | Bind group slot.                                                                    |
| recorder.setBindGroup.group          | `GPUBindGroup \| null`                             |        ✔ | —                            | Native bind group or `null`.                                                        |
| recorder.setBindGroup.dynamicOffsets | `readonly GPUBufferDynamicOffset[] \| Uint32Array` |        ✖ | `undefined`                  | Forwarded to `setBindGroup`.                                                        |
| recorder.setVertexBuffer.slot        | `number`                                           |        ✔ | —                            | Vertex buffer slot.                                                                 |
| recorder.setVertexBuffer.buffer      | `Buffer \| GPUBuffer \| null`                      |        ✔ | —                            | Core `Buffer` is unwrapped to `.gpu`; native buffer and `null` pass through.        |
| recorder.setVertexBuffer.offset      | `number`                                           |        ✖ | `0`                          | Byte offset.                                                                        |
| recorder.setVertexBuffer.size        | `GPUSize64`                                        |        ✖ | `undefined`                  | Byte size.                                                                          |
| recorder.draw\.options.vertexCount   | `number`                                           |        ✔ | —                            | Object overload vertex count.                                                       |
| recorder.draw\.options.instanceCount | `number`                                           |        ✖ | `1`                          | Object overload instance count.                                                     |
| recorder.draw\.options.firstVertex   | `number`                                           |        ✖ | `0`                          | Object overload first vertex.                                                       |
| recorder.draw\.options.firstInstance | `number`                                           |        ✖ | `0`                          | Object overload first instance.                                                     |

**Returns:** `createRenderBundle()` returns `GPURenderBundle`; recorder methods return `void`.

**Throws:** No custom `VGPU-*` errors are thrown here. Native WebGPU validation errors occur for incompatible formats, pipelines, bind groups, buffers, or draw parameters. main API (`vgpu`) stale errors (`VGPU-R3-BUNDLE-STALE`, `VGPU-R3-BUNDLE-INVALID`) are available only through `bundle(gpu)` / `FramePass.bundles()`.

## Examples

```ts
import { init } from "vgpu/mock";
import { createRenderBundle } from "vgpu/core";

const gpu = await init();
const bundle = createRenderBundle(gpu.device, {
  label: "empty",
  colorFormats: ["rgba8unorm"],
  sampleCount: 1,
  record(recorder) {
    void recorder;
  },
});
void bundle;
```

```ts
import { init, target } from "vgpu/mock";
import { createRenderBundle } from "vgpu/core";

const gpu = await init();
const colorTarget = target(gpu, { size: [16, 16] });
const bundle = createRenderBundle(gpu.device, {
  colorFormats: colorTarget.colors.map((color) => color.format),
  depthStencilFormat: colorTarget.depth?.format,
  sampleCount: colorTarget.sampleCount,
  record(recorder) {
    recorder.draw({ vertexCount: 0, instanceCount: 0 });
  },
});
void bundle;
```

## Notes

* This helper intentionally does not know about main API (`vgpu`) `Draw`, `Effect`, or `Target`; you must supply formats and native commands yourself.
* Use `bundle(gpu)` for public API examples unless you are already managing native pipelines.
* `RenderBundleRecorder.draw(number, ...)` defaults to `(instanceCount=1, firstVertex=0, firstInstance=0)`; object overload has the same defaults.
* **See also:** `Bundle`, `FramePass.bundles`, `Draw`, `Target`.


---

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)