---
title: bind and explicit binding helpers
description: `bind`, `createBindGroupLayout`, `createPipelineLayout`, `createBindGroup`, and `createSampler` are thin core helpers for explicit WebGPU binding. Use them when you want raw WebGPU layouts, bind groups, and samplers without shader reflection or `layout: "auto"` inference.
---

# bind and explicit binding helpers



## Import

```ts
import { bind, createBindGroup, createBindGroupLayout, createPipelineLayout, createSampler } from "vgpu/core";
```

## Signature

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

type BindVisibility = GPUShaderStageFlags | string | readonly ("vertex" | "fragment" | "compute")[];
type DeviceLike = GPUDevice | Device | { readonly gpu: GPUDevice };

interface CreateBindGroupLayoutOptions {
  readonly label?: string;
  readonly entries: readonly GPUBindGroupLayoutEntry[];
}

interface CreatePipelineLayoutOptions {
  readonly label?: string;
  readonly bindGroups: readonly GPUBindGroupLayout[];
}

interface CreateBindGroupOptions {
  readonly label?: string;
  readonly layout: GPUBindGroupLayout;
  readonly entries: readonly GPUBindGroupEntry[];
}

interface SamplerDescriptorWithSugar extends GPUSamplerDescriptor {
  readonly filter?: "linear" | "nearest";
  readonly wrap?: "clamp" | "repeat" | "mirror";
}

declare function createBindGroupLayout(device: DeviceLike, opts: CreateBindGroupLayoutOptions): GPUBindGroupLayout;
declare function createPipelineLayout(device: DeviceLike, opts: CreatePipelineLayoutOptions): GPUPipelineLayout;
declare function createBindGroup(device: DeviceLike, opts: CreateBindGroupOptions): GPUBindGroup;
declare function createSampler(device: DeviceLike, descriptor?: SamplerDescriptorWithSugar): GPUSampler;

declare const bind: {
  readonly uniform: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
  readonly storage: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
  readonly readonlyStorage: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
  readonly texture: (binding: number, visibility: BindVisibility, opts?: GPUTextureBindingLayout) => GPUBindGroupLayoutEntry;
  readonly storageTexture: (binding: number, visibility: BindVisibility, opts: GPUStorageTextureBindingLayout) => GPUBindGroupLayoutEntry;
  readonly sampler: (binding: number, visibility: BindVisibility, opts?: GPUSamplerBindingLayout) => GPUBindGroupLayoutEntry;
  readonly resource: (binding: number, value: Buffer | Texture | GPUBuffer | GPUBufferBinding | GPUBindingResource | unknown) => GPUBindGroupEntry;
};
```

## Parameters

### Shared parameters

| Param      | Type                                                                                | Required | Default | Notes                                                                                                             |                                                                                                                |
| ---------- | ----------------------------------------------------------------------------------- | -------: | ------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| device     | `DeviceLike`                                                                        |        ✔ | —       | A raw `GPUDevice`, a vgpu `Device`, or an object with `.gpu: GPUDevice`. Helpers unwrap it before calling WebGPU. |                                                                                                                |
| binding    | `number`                                                                            |        ✔ | —       | Explicit non-negative integer `@binding(n)`. Invalid values throw `VGPU-CORE-BINDING-INVALID`.                    |                                                                                                                |
| visibility | `GPUShaderStageFlags \| string \| readonly ("vertex" \| "fragment" \| "compute")[]` |        ✔ | —       | Numeric flags pass through. Strings split on \`                                                                   | `, comma, or whitespace. Arrays use stage names directly. Unknown names throw `VGPU-CORE-VISIBILITY-INVALID\`. |

### `createBindGroupLayout(device, opts)`

| Param        | Type                                 | Required | Default     | Notes                                                                                               |
| ------------ | ------------------------------------ | -------: | ----------- | --------------------------------------------------------------------------------------------------- |
| opts         | `CreateBindGroupLayoutOptions`       |        ✔ | —           | Layout descriptor wrapper.                                                                          |
| opts.label   | `string`                             |        ✖ | `undefined` | Forwarded to `GPUDevice.createBindGroupLayout`.                                                     |
| opts.entries | `readonly GPUBindGroupLayoutEntry[]` |        ✔ | —           | Copied with `[...opts.entries]` before calling WebGPU; metadata is attached to the returned layout. |

### `createPipelineLayout(device, opts)`

| Param           | Type                            | Required | Default     | Notes                                                         |
| --------------- | ------------------------------- | -------: | ----------- | ------------------------------------------------------------- |
| opts            | `CreatePipelineLayoutOptions`   |        ✔ | —           | Pipeline layout descriptor wrapper.                           |
| opts.label      | `string`                        |        ✖ | `undefined` | Forwarded to `GPUDevice.createPipelineLayout`.                |
| opts.bindGroups | `readonly GPUBindGroupLayout[]` |        ✔ | —           | Copied to WebGPU as `bindGroupLayouts: [...opts.bindGroups]`. |

### `createBindGroup(device, opts)`

| Param        | Type                           | Required | Default     | Notes                                                                                                                                      |
| ------------ | ------------------------------ | -------: | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| opts         | `CreateBindGroupOptions`       |        ✔ | —           | Bind group descriptor wrapper.                                                                                                             |
| opts.label   | `string`                       |        ✖ | `undefined` | Forwarded to `GPUDevice.createBindGroup`.                                                                                                  |
| opts.layout  | `GPUBindGroupLayout`           |        ✔ | —           | Required explicit layout. Missing/falsy layout throws `VGPU-CORE-BIND-GROUP-LAYOUT-REQUIRED`; vgpu never uses bind group `layout: "auto"`. |
| opts.entries | `readonly GPUBindGroupEntry[]` |        ✔ | —           | Copied with `[...opts.entries]` before calling WebGPU; metadata is attached to the returned bind group.                                    |

### `createSampler(device, descriptor?)`

| Param                    | Type                              | Required | Default        | Notes                                                                                                                                                             |
| ------------------------ | --------------------------------- | -------: | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| descriptor               | `SamplerDescriptorWithSugar`      |        ✖ | `{}`           | Raw `GPUSamplerDescriptor` plus vgpu sugar. Sugar is stripped before calling WebGPU.                                                                              |
| descriptor.filter        | `"linear" \| "nearest"`           |        ✖ | `undefined`    | Expands to `magFilter` and `minFilter` only. Raw `magFilter`/`minFilter` override this per key.                                                                   |
| descriptor.wrap          | `"clamp" \| "repeat" \| "mirror"` |        ✖ | `undefined`    | Expands to all three address modes: `"clamp"` → `"clamp-to-edge"`, `"repeat"` → `"repeat"`, `"mirror"` → `"mirror-repeat"`. Raw address fields override per axis. |
| descriptor.mipmapFilter  | `GPUMipmapFilterMode`             |        ✖ | WebGPU default | Not set by `filter`; pass `"linear"` explicitly for anisotropic sampling.                                                                                         |
| descriptor.maxAnisotropy | `number`                          |        ✖ | WebGPU default | If `filter` sugar is used and `maxAnisotropy > 1`, all three filters must resolve to `"linear"`.                                                                  |

### `bind.*` layout entry helpers

| Param                     | Type                                   | Required | Default | Notes                                                                                                                                                                                   |
| ------------------------- | -------------------------------------- | -------: | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| bind.uniform opts         | `Omit<GPUBufferBindingLayout, "type">` |        ✖ | `{}`    | Returns `{ binding, visibility, buffer: { ...opts, type: "uniform" } }`.                                                                                                                |
| bind.storage opts         | `Omit<GPUBufferBindingLayout, "type">` |        ✖ | `{}`    | Returns `{ binding, visibility, buffer: { ...opts, type: "storage" } }`.                                                                                                                |
| bind.readonlyStorage opts | `Omit<GPUBufferBindingLayout, "type">` |        ✖ | `{}`    | Returns `{ binding, visibility, buffer: { ...opts, type: "read-only-storage" } }`.                                                                                                      |
| bind.texture opts         | `GPUTextureBindingLayout`              |        ✖ | `{}`    | Returns `{ binding, visibility, texture: opts }`.                                                                                                                                       |
| bind.storageTexture opts  | `GPUStorageTextureBindingLayout`       |        ✔ | —       | Returns `{ binding, visibility, storageTexture: opts }`; storage texture layout fields are not inferred.                                                                                |
| bind.sampler opts         | `GPUSamplerBindingLayout`              |        ✖ | `{}`    | Returns `{ binding, visibility, sampler: opts }`.                                                                                                                                       |
| bind.resource value       | `unknown`                              |        ✔ | —       | Converts vgpu `Buffer` to `{ buffer: buffer.gpu }`, vgpu `Texture`/texture-like objects to `createView()`, raw `GPUBuffer` to `{ buffer }`, and passes through other binding resources. |

**Returns:**

* `createBindGroupLayout(...)` returns `GPUBindGroupLayout` with vgpu layout metadata attached.
* `createPipelineLayout(...)` returns raw `GPUPipelineLayout`.
* `createBindGroup(...)` returns `GPUBindGroup` with vgpu bind-group metadata attached.
* `createSampler(...)` returns raw `GPUSampler`.
* `bind.uniform(...)`, `bind.storage(...)`, `bind.readonlyStorage(...)`, `bind.texture(...)`, `bind.storageTexture(...)`, and `bind.sampler(...)` return `GPUBindGroupLayoutEntry`.
* `bind.resource(...)` returns `GPUBindGroupEntry`.

**Throws:**

* `VGPU-CORE-BIND-GROUP-LAYOUT-REQUIRED` when `createBindGroup(...)` receives a missing/falsy `opts.layout` — create and pass an explicit `GPUBindGroupLayout`.
* `VGPU-CORE-SAMPLER-ANISOTROPY-FILTERS` when `createSampler(...)` uses `filter` sugar with `maxAnisotropy > 1` but the resolved `magFilter`, `minFilter`, and `mipmapFilter` are not all `"linear"` — add `mipmapFilter: "linear"` and avoid raw non-linear overrides.
* `VGPU-CORE-BINDING-INVALID` when any `bind.*` helper receives a negative, non-integer, or otherwise invalid `binding` — pass an explicit non-negative integer.
* `VGPU-CORE-VISIBILITY-INVALID` when a string/array visibility contains an unknown shader stage name — use only `"vertex"`, `"fragment"`, and `"compute"`, or pass numeric `GPUShaderStageFlags`.
* Native WebGPU validation errors may occur for descriptor/layout/resource incompatibilities — fix the WebGPU descriptor or resource usage.

## Examples

```ts
import { bind, createBindGroup, createBindGroupLayout, createPipelineLayout, createSampler } from "vgpu/core";
import { createMockAdapter } from "vgpu/mock";

const device = await createMockAdapter().requestDevice();
const uniformBuffer = device.createBuffer({ size: 64, usage: ["uniform", "copy_dst"] });
const texture = device.createTexture({
  size: [1, 1],
  format: "rgba8unorm",
  usage: ["texture_binding", "copy_src"],
});
const sampler = createSampler(device, { filter: "linear", wrap: "clamp" });

const groupLayout = createBindGroupLayout(device, {
  label: "scene.group0",
  entries: [
    bind.uniform(0, "vertex|fragment"),
    bind.texture(1, "fragment", { sampleType: "float" }),
    bind.sampler(2, ["fragment"], { type: "filtering" }),
  ],
});

const pipelineLayout = createPipelineLayout(device, { bindGroups: [groupLayout] });
const group = createBindGroup(device, {
  layout: groupLayout,
  entries: [
    bind.resource(0, uniformBuffer),
    bind.resource(1, texture),
    bind.resource(2, sampler),
  ],
});

console.log(pipelineLayout, group);
device.destroy();
```

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

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

const mixedSampler = createSampler(device, {
  filter: "linear",
  wrap: "repeat",
  magFilter: "nearest", // raw field wins for this key only
});

const anisotropicSampler = createSampler(device, {
  filter: "linear",
  mipmapFilter: "linear",
  maxAnisotropy: 16,
});

console.log(mixedSampler, anisotropicSampler);
device.destroy();
```

```ts
import { bind, createBindGroupLayout } from "vgpu/core";
import { createMockAdapter } from "vgpu/mock";

const device = await createMockAdapter().requestDevice();
const computeLayout = createBindGroupLayout(device, {
  entries: [
    bind.storage(0, "compute", { hasDynamicOffset: true }),
    bind.storageTexture(1, "compute", { access: "write-only", format: "rgba8unorm", viewDimension: "2d" }),
  ],
});

console.log(computeLayout);
device.destroy();
```

## Notes

* These helpers are explicit by design: no WGSL reflection, no named binding maps, no hidden resource creation, and no `layout: "auto"` bind groups.
* `filter` sugar does not set `mipmapFilter`. For anisotropic sampling, spell trilinear filtering explicitly.
* Raw WebGPU descriptor fields override sugar per key, not all-or-nothing.
* `bind.resource(...)` creates a default texture view for vgpu `Texture`; use a raw `GPUTextureView` when you need a non-default mip/array/format view.
* Keep shader `@group/@binding` numbers synchronized with helper `binding` numbers; vgpu does not reflect or renumber them here.
* **See also:** `Device`, `Buffer`, `Texture`, `Queue`, `VGPUError`.


---

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)