{"id":13534298,"url":"https://github.com/CodyJasonBennett/four","last_synced_at":"2025-04-01T22:31:25.825Z","repository":{"id":63784490,"uuid":"570563987","full_name":"CodyJasonBennett/four","owner":"CodyJasonBennett","description":"💎 Minimal three.js alternative.","archived":false,"fork":false,"pushed_at":"2024-05-06T18:19:52.000Z","size":2813,"stargazers_count":304,"open_issues_count":1,"forks_count":13,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-10-29T16:59:28.526Z","etag":null,"topics":["3d","compute","graphics","minimal","shaders","three","webgl","webgpu","webxr"],"latest_commit_sha":null,"homepage":"https://npmjs.com/fourwastaken","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CodyJasonBennett.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["CodyJasonBennett"]}},"created_at":"2022-11-25T13:49:23.000Z","updated_at":"2024-10-27T14:30:15.000Z","dependencies_parsed_at":"2023-11-08T12:29:44.316Z","dependency_job_id":"73c20add-086e-460b-bfeb-c85cbb5971b9","html_url":"https://github.com/CodyJasonBennett/four","commit_stats":{"total_commits":88,"total_committers":3,"mean_commits":"29.333333333333332","dds":"0.34090909090909094","last_synced_commit":"cd6c5ba935dd89cd00fe3e1d55e550d16a96b64d"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyJasonBennett%2Ffour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyJasonBennett%2Ffour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyJasonBennett%2Ffour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyJasonBennett%2Ffour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CodyJasonBennett","download_url":"https://codeload.github.com/CodyJasonBennett/four/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246720569,"owners_count":20822916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","compute","graphics","minimal","shaders","three","webgl","webgpu","webxr"],"created_at":"2024-08-01T07:01:29.954Z","updated_at":"2025-04-01T22:31:25.420Z","avatar_url":"https://github.com/CodyJasonBennett.png","language":"TypeScript","funding_links":["https://github.com/sponsors/CodyJasonBennett"],"categories":["TypeScript","Libraries"],"sub_categories":["JavaScript"],"readme":"[![Size](https://img.shields.io/badge/dynamic/json?label=gzip\u0026style=flat\u0026colorA=000000\u0026colorB=000000\u0026query=$.size.compressedSize\u0026url=https://deno.bundlejs.com/?q=fourwastaken)](https://unpkg.com/fourwastaken)\n[![Version](https://img.shields.io/npm/v/fourwastaken?style=flat\u0026colorA=000000\u0026colorB=000000)](https://npmjs.com/package/fourwastaken)\n[![Downloads](https://img.shields.io/npm/dt/fourwastaken.svg?style=flat\u0026colorA=000000\u0026colorB=000000)](https://npmjs.com/package/fourwastaken)\n\n# four\n\nMinimal three.js alternative.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Getting Started](#getting-started)\n- [Object3D](#object3d)\n  - [Vector3](#vector3)\n  - [Quaternion](#quaternion)\n  - [Matrix4](#matrix4)\n- [Mesh](#mesh)\n- [Geometry](#geometry)\n  - [Attribute](#attribute)\n- [Material](#material)\n  - [Uniforms](#uniforms)\n  - [Blending](#blending)\n- [Texture](#texture)\n  - [Sampler](#sampler)\n- [RenderTarget](#rendertarget)\n- [Camera](#camera)\n  - [Frustum](#frustum)\n  - [PerspectiveCamera](#perspectivecamera)\n  - [OrthographicCamera](#orthographiccamera)\n- [Rendering](#rendering)\n  - [Instancing](#instancing)\n  - [Compute](#compute)\n\n## Installation\n\nTo install, use your preferred package manager or CDN:\n\n```bash\nnpm install four@npm:fourwastaken\nyarn add four@npm:fourwastaken\npnpm add four@npm:fourwastaken\n```\n\n```html\n\u003cscript type=\"module\"\u003e\n  import * as FOUR from 'https://unpkg.com/fourwastaken'\n\u003c/script\u003e\n```\n\n\u003e **Note**: Vite may have issues consuming WebGPU code which relies on top-level await via ESM. This is well supported since 2021, but you may need to use [vite-plugin-top-level-await](https://github.com/Menci/vite-plugin-top-level-await) to use this library with `vite.optimizeDeps`.\n\n## Getting Started\n\nThe following creates a renderer, camera, and renders a red cube:\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGL example\u003c/summary\u003e\n\n```ts\nimport { WebGLRenderer, PerspectiveCamera, Geometry, Material, Mesh } from 'four'\n\nconst renderer = new WebGLRenderer()\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.canvas)\n\nconst camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight)\ncamera.position.z = 5\n\nconst geometry = new Geometry({\n  position: {\n    size: 3,\n    data: new Float32Array([\n      0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,\n      -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5,\n      0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5,\n      0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5,\n      -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5,\n      -0.5, -0.5, -0.5, 0.5, -0.5,\n    ]),\n  },\n})\nconst material = new Material({\n  vertex: /* glsl */ `#version 300 es\n    uniform mat4 projectionMatrix;\n    uniform mat4 modelViewMatrix;\n    in vec3 position;\n    void main() {\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);\n    }\n  `,\n  fragment: /* glsl */ `#version 300 es\n    out lowp vec4 color;\n    void main() {\n      color = vec4(1, 0, 0, 1);\n    }\n  `,\n})\nconst mesh = new Mesh(geometry, material)\n\nrenderer.render(mesh, camera)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGPU example\u003c/summary\u003e\n\n```ts\nimport { WebGPURenderer, PerspectiveCamera, Geometry, Material, Mesh } from 'four'\n\nconst renderer = new WebGPURenderer()\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.canvas)\n\nconst camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight)\ncamera.position.z = 5\n\nconst geometry = new Geometry({\n  position: {\n    size: 3,\n    data: new Float32Array([\n      0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, -0.5,\n      -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5,\n      0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5,\n      0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5, -0.5,\n      -0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, -0.5,\n      -0.5, -0.5, -0.5, 0.5, -0.5,\n    ]),\n  },\n})\nconst material = new Material({\n  vertex: /* wgsl */ `\n    struct Uniforms {\n      projectionMatrix: mat4x4\u003cf32\u003e,\n      modelViewMatrix: mat4x4\u003cf32\u003e,\n    };\n    @group(0) @binding(0) var\u003cuniform\u003e uniforms: Uniforms;\n\n    @vertex\n    fn main(@location(0) position: vec3\u003cf32\u003e) -\u003e @builtin(position) vec4\u003cf32\u003e {\n      return uniforms.projectionMatrix * uniforms.modelViewMatrix * vec4(position, 1);\n    }\n  `,\n  fragment: /* wgsl */ `\n    @fragment\n    fn main() -\u003e @location(0) vec4\u003cf32\u003e {\n      return vec4(1, 0, 0, 1);\n    }\n  `,\n})\nconst mesh = new Mesh(geometry, material)\n\nrenderer.render(mesh, camera)\n```\n\n\u003c/details\u003e\n\n## Object3D\n\nAn `Object3D` represents a basic 3D object and its transforms. Objects are linked via their `parent` and `children` properties, constructing a rooted scene-graph.\n\n```ts\nconst object = new Object3D()\nobject.add(new Object3D(), new Object3D())\nobject.traverse((node) =\u003e {\n  if (node !== object) object.remove(node)\n  if (!node.visible) return true\n})\n```\n\n### Vector3\n\nA `Vector3` represents a three-dimensional (x, y, z) vector and describes local position in `Object3D.position`. It is also used to control local scale in `Object3D.scale`.\n\n```ts\nobject.position.set(1, 2, 3)\nobject.position.x = 4\nobject.position[0] = 5\n```\n\n### Quaternion\n\nA `Quaternion` represents a four-dimensional vector with a rotation axis (x, y, z) and magnitude (w) and describes local orientation in `Object3D.quaternion`.\n\n```ts\nobject.quaternion.set(0, 0, 0, 1)\nobject.quaternion.fromEuler(Math.PI / 2, 0, 0)\nobject.quaternion.x *= -1\nobject.quaternion[0] *= -1\n```\n\n### Matrix4\n\nA `Matrix4` represents a 4x4 transformation matrix and describes world transforms in `Object3D.matrix`.\n\n```ts\nobject.matrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 2, 3, 1)\nobject.matrix[12] = 4\nobject.matrix.invert()\nobject.matrix.identity()\n```\n\n## Mesh\n\nA `Mesh` contains a `Geometry` and `Material` to describe visual behavior, and can be manipulated in 3D as an `Object3D`.\n\n```ts\nconst geometry = new Geometry({ ... })\nconst material = new Material({ ... })\nconst mesh = new Mesh(geometry, material)\n```\n\n## Geometry\n\nA `Geometry` contains an `Attribute` list of vertex or storage buffer data, with a GPU buffer allocated for each `Attribute`.\n\n```ts\nconst geometry = new Geometry({\n  position: { size: 2, data: new Float32Array([-1, -1, 3, -1, -1, 3]) },\n  uv: { size: 2, data: new Float32Array([0, 0, 2, 0, 0, 2]) },\n  index: { size: 1, data: new Uint16Array([0, 1, 2]) },\n})\n```\n\nA `DrawRange` can also be configured to control rendering without submitting vertex data. This is useful for GPU-computed geometry or vertex pulling, as demonstrated in the fullscreen demos.\n\n```ts\nconst geometry = new Geometry()\ngeometry.drawRange = { start: 0, count: 3 } // renders 3 vertices at starting index 0\n```\n\n### Attribute\n\nAn `Attribute` defines a data view, its per-vertex size, and an optional per-instance divisor (see [instancing](#instancing)).\n\n```ts\n// Creates a 4x4 instance matrix for 2 instances\n{\n  data: new Float32Array([\n    1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,\n    1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,\n  ]),\n  size: 16,\n  divisor: 1,\n}\n```\n\n## Material\n\nA `Material` describes a program or shader interface for rasterization and compute (see [compute](#compute)), defining a `vertex` and `fragment` or `compute` shader, respectively.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGL example\u003c/summary\u003e\n\n```ts\nconst material = new Material({\n  vertex: /* glsl */ `#version 300 es\n    uniform mat4 projectionMatrix;\n    uniform mat4 modelViewMatrix;\n    in vec3 position;\n    void main() {\n      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);\n    }\n  `,\n  fragment: /* glsl */ `#version 300 es\n    out lowp vec4 color;\n    void main() {\n      color = vec4(1, 0, 0, 1);\n    }\n  `,\n  side: 'front',\n  transparent: false,\n  depthTest: true,\n  depthWrite: true,\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGPU example\u003c/summary\u003e\n\n```ts\nconst material = new Material({\n  vertex: /* wgsl */ `\n    struct Uniforms {\n      projectionMatrix: mat4x4\u003cf32\u003e,\n      modelViewMatrix: mat4x4\u003cf32\u003e,\n    };\n    @group(0) @binding(0) var\u003cuniform\u003e uniforms: Uniforms;\n\n    @vertex\n    fn main(@location(0) position: vec3\u003cf32\u003e) -\u003e @builtin(position) vec4\u003cf32\u003e {\n      return uniforms.projectionMatrix * uniforms.modelViewMatrix * vec4(position, 1);\n    }\n  `,\n  fragment: /* wgsl */ `\n    @fragment\n    fn main() -\u003e @location(0) vec4\u003cf32\u003e {\n      return vec4(1, 0, 0, 1);\n    }\n  `,\n  side: 'front',\n  transparent: false,\n  depthTest: true,\n  depthWrite: true,\n})\n```\n\n\u003c/details\u003e\n\n### Uniforms\n\nThe following uniforms are built-in and will be automatically populated when specified:\n\n| Type     | Name             | Description                                        | Conversion                 |\n| -------- | ---------------- | -------------------------------------------------- | -------------------------- |\n| `mat4x4` | modelMatrix      | world-space mesh transform                         | local space =\u003e world space |\n| `mat4x4` | projectionMatrix | clip-space camera projection                       | view space =\u003e clip space   |\n| `mat4x4` | viewMatrix       | inverse camera transform                           | world space =\u003e view space  |\n| `mat4x4` | modelViewMatrix  | premultiplied model-view transform                 | local space =\u003e view space  |\n| `mat4x4` | normalMatrix     | isotropic inverse model-view or \"normal\" transform | local space =\u003e view space  |\n\nIn WebGPU, uniforms are bound to a single uniform buffer, preceded by storage buffers, and followed by sampler-texture for texture uniforms.\n\n```wgsl\n// Storage buffers\n@group(0) @binding(0)\nvar\u003cstorage, read_write\u003e data: array\u003cvec2\u003cf32\u003e\u003e;\n\n// Uniform buffer\nstruct Uniforms {\n  time: f32,\n};\n@group(0) @binding(1) var\u003cuniform\u003e uniforms: Uniforms;\n\n// Texture bindings\n@group(0) @binding(2) var sample: sampler;\n@group(0) @binding(3) var color: texture_2d\u003cf32\u003e;\n\n@group(0) @binding(4) var sample_2: sampler;\n@group(0) @binding(5) var color_2: texture_2d\u003cf32\u003e;\n```\n\n### Blending\n\nBy default, opaque meshes do not blend but replace values, and transparent meshes alpha blend by the following blend equation:\n\n```ts\nmaterial.blending = {\n  color: {\n    operation: 'add',\n    srcFactor: 'src-alpha',\n    dstFactor: 'one-minus-src-alpha',\n  },\n  alpha: {\n    operation: 'add',\n    srcFactor: 'one',\n    dstFactor: 'one-minus-src-alpha',\n  },\n}\n```\n\nThis gets applied to the final fragment color as `src * srcFactor + dst * dstFactor`, assuming a premultiplied alpha.\n\nCustom blending can be used for postprocessing and various VFX. The following are the most common configurations:\n\n| Blend Mode     | BlendOperation     | BlendFactor (src)     | BlendFactor (dst) |\n| -------------- | ------------------ | --------------------- | ----------------- |\n| Additive       | `add`              | `src-alpha`           | `one`             |\n| Subtractive    | `reverse-subtract` | `src-alpha`           | `one`             |\n| Multiply       | `add`              | `dst-color`           | `zero`            |\n| Screen         | `add`              | `one-minus-src-color` | `one`             |\n| Maximize       | `max`              | `src-alpha`           | `dst-alpha`       |\n| Custom         | `add`              | `one`                 | `one`             |\n| Local Additive | `add`              | `dst-alpha`           | `one`             |\n| Disabled       | `add`              | `one`                 | `zero`            |\n\n## Texture\n\nA `Texture` transports or stores image or video data to the GPU as well as data like normals or depth.\n\n```ts\nconst pixel = new Uint8ClampedArray([76, 51, 128, 255])\nconst image = new ImageData(pixel, 1, 1)\nconst texture = new Texture(image)\n```\n\n### Sampler\n\nA `Sampler` configures texel filtering and transforms for a texture, and can be used to sample a texture multiple times with different configurations in a shader.\n\n```ts\nconst sampler = new Sampler({\n  magFilter: 'nearest',\n  minFilter: 'nearest',\n  wrapS: 'clamp',\n  wrapT: 'clamp',\n  anisotropy: 1,\n})\ntexture.sampler = sampler\n```\n\n## RenderTarget\n\nA `RenderTarget` constructs a frame buffer object which can be drawn to, similar to the canvas itself. Unlike the canvas, render targets can have multiple attachments or texture channels, configurable as the third argument `count`, enabling efficient use of techniques like deferred rendering and postprocessing.\n\n```ts\n// Create render target with 4 channels\nconst width = window.innerWidth\nconst height = window.innerHeight\nconst count = 4\nconst renderTarget = new RenderTarget(width, height, count)\n\n// Resize with page\nwindow.addEventListener('resize', () =\u003e {\n  const width = window.innerWidth\n  const height = window.innerHeight\n  renderTarget.setSize(width, height)\n})\n\n// Bind and render to render target\nrenderer.setRenderTarget(renderTarget)\nrenderer.render(scene, camera)\n\n// Unbind to canvas\nrenderer.setRenderTarget(null)\n```\n\n## Camera\n\nA `Camera` contains matrices and a frustum for projection transforms and queries. The type of projection is defined by `Camera.projectionMatrix`.\n\n### Frustum\n\nA `Frustum` contains clipping planes used for frustum culling and queries, set from a projectionViewMatrix.\n\n```ts\ncamera.frustum.fromMatrix4(camera.projectionViewMatrix)\nif (camera.frustum.contains(mesh)) {\n  // ...\n}\n```\n\n### PerspectiveCamera\n\nA `PerspectiveCamera` calculates a perspective or non-linear `projectionMatrix`, where objects appear smaller by distance.\n\n```ts\nconst fov = 75\nconst aspect = canvas.width / canvas.height\nconst near = 0.1\nconst far = 1000\nconst camera = new PerspectiveCamera(fov, aspect, near, far)\n```\n\n### OrthographicCamera\n\nAn `OrthographicCamera` calculates an orthographic or linear `projectionMatrix`, where objects are unaffected by distance.\n\n```ts\nconst near = 0.1\nconst far = 1000\nconst left = -(canvas.width / 2)\nconst right = canvas.width / 2\nconst bottom = -(canvas.height / 2)\nconst top = canvas.height / 2\nconst camera = new OrthographicCamera(near, far, left, right, bottom, top)\n```\n\n## Rendering\n\nFour supports WebGL 2 and WebGPU with `WebGLRenderer` and `WebGPURenderer`, respectively, and implements a shared API for rendering and compute.\n\n```ts\nconst renderer = new WebGLRenderer()\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.canvas)\n\n//\n\nconst renderer = new WebGPURenderer()\nrenderer.setSize(window.innerWidth, window.innerHeight)\ndocument.body.appendChild(renderer.canvas)\n```\n\n### Instancing\n\nFour instances by default, which better aligns with WebGPU. Instanced rendering rasterizes multiple vertex primitives with the same shader interface to render multiple meshes at the cost of one.\n\nYou can specify the number of instances to render with `Mesh.instances` and add variance or control each instance with `Attribute.divisor` to specify a per-instance divisor. A divisor of one will be used by a single instance, and a divisor greater than one will be used by multiple instances.\n\n\u003e **Note**: Attributes can only allocate primitive types in WebGPU ([gpuweb/gpuweb#1652](https://github.com/gpuweb/gpuweb/issues/1652)), so you must allocate and index storage or uniform buffers via the `instance_index` built-in for complex types like matrices.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGL example\u003c/summary\u003e\n\n```ts\nconst geometry = new Geometry({\n  position: { size: 3, data: new Float32Array([...]) },\n  instanceMatrix: { divisor: 1, size: 16, data: new Float32Array([...]) }\n})\n\nconst material = new Material({\n  vertex: /* glsl */`#version 300 es\n    uniform mat4 projectionMatrix;\n    uniform mat4 modelViewMatrix;\n    in mat4 instanceMatrix;\n    in vec3 position;\n    void main() {\n      gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1);\n    }\n  `,\n  fragment: /* glsl */`#version 300 es\n    out lowp vec4 color;\n    void main() {\n      color = vec4(1, 0, 0, 1);\n    }\n  `\n})\n\nconst mesh = new Mesh(geometry, material)\nmesh.instances = 2\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eShow WebGPU example\u003c/summary\u003e\n\n```ts\nconst geometry = new Geometry({\n  position: { size: 3, data: new Float32Array([...]) },\n})\n\nconst material = new Material({\n  uniforms: {\n    instanceMatrix: new Float32Array([...]),\n  },\n  vertex: /* wgsl */ `\n    struct Uniforms {\n      projectionMatrix: mat4x4\u003cf32\u003e,\n      modelViewMatrix: mat4x4\u003cf32\u003e,\n      instanceMatrix: array\u003cmat4x4\u003cf32\u003e, 2\u003e,\n    };\n    @group(0) @binding(0) var\u003cuniform\u003e uniforms: Uniforms;\n\n    @vertex\n    fn main(\n      @builtin(instance_index) instanceID: u32,\n      @location(0) position: vec3\u003cf32\u003e,\n    ) -\u003e @builtin(position) vec4\u003cf32\u003e {\n      return uniforms.projectionMatrix * uniforms.modelViewMatrix * uniforms.instanceMatrix[instanceID] * vec4(position, 1.0);\n    }\n  `,\n  fragment: /* wgsl */ `\n    @fragment\n    fn main() -\u003e @location(0) vec4\u003cf32\u003e {\n      return vec4(1, 0, 0, 1);\n    }\n  `,\n})\n\nconst mesh = new Mesh(geometry, material)\nmesh.instances = 2\n```\n\n\u003c/details\u003e\n\n### Compute\n\nFour supports compute for both WebGL and WebGPU via transform feedback and compute pipelines, respectively. This can be used in lieu of pixel shaders to write directly to buffer storage without any CPU reads/writes to textures. Useful for high precision compute or large simulations where texture memory is limited.\n\nThe following populates geometry buffers on the GPU, computing a fullscreen triangle geometry:\n\n\u003cdetails\u003e\n  \u003csummary\u003eShow WebGL example\u003c/summary\u003e\n\n```ts\nconst geometry = new Geometry({\n  position: { size: 2, data: new Float32Array(6) },\n  uv: { size: 2, data: new Float32Array(6) },\n})\nconst computeMaterial = new Material({\n  compute: /* glsl */ `#version 300 es\n    out vec2 uv;\n    out vec2 position;\n\n    void main() {\n      uv = vec2(gl_VertexID \u003c\u003c 1 \u0026 2, gl_VertexID \u0026 2);\n      position = uv * 2.0 - 1.0;\n    }\n  `,\n})\nconst mesh = new Mesh(geometry, computeMaterial)\n\nrenderer.compute(mesh)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eShow WebGPU example\u003c/summary\u003e\n\n```ts\nconst geometry = new Geometry({\n  position: { size: 2, data: new Float32Array(6) },\n  uv: { size: 2, data: new Float32Array(6) },\n})\nconst computeMaterial = new Material({\n  compute: /* wgsl */ `\n    @group(0) @binding(0)\n    var\u003cstorage, read_write\u003e position: array\u003cvec2\u003cf32\u003e\u003e;\n\n    @group(0) @binding(1)\n    var\u003cstorage, read_write\u003e uv: array\u003cvec2\u003cf32\u003e\u003e;\n\n    @compute @workgroup_size(64)\n    fn main(@builtin(local_invocation_index) i: u32) {\n      uv[i] = vec2\u003cf32\u003e(vec2((i \u003c\u003c 1) \u0026 2, i \u0026 2));\n      position[i] = uv[i] * 2 - 1;\n    }\n  `,\n})\nconst mesh = new Mesh(geometry, computeMaterial)\n\nrenderer.compute(mesh)\n```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodyJasonBennett%2Ffour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCodyJasonBennett%2Ffour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodyJasonBennett%2Ffour/lists"}