{"id":13626399,"url":"https://github.com/amandaghassaei/gpu-io","last_synced_at":"2025-05-15T15:04:26.573Z","repository":{"id":57752521,"uuid":"309830112","full_name":"amandaghassaei/gpu-io","owner":"amandaghassaei","description":"A GPU-accelerated computing library for running physics simulations and other GPGPU computations in a web browser.","archived":false,"fork":false,"pushed_at":"2024-01-31T23:07:13.000Z","size":24605,"stargazers_count":1247,"open_issues_count":4,"forks_count":47,"subscribers_count":21,"default_branch":"main","last_synced_at":"2025-05-08T14:46:25.626Z","etag":null,"topics":["canvas","glsl","gpgpu","gpu","parallel","particles","physics","shader","shaders","simulation","threejs","webgl","webgl1","webgl2"],"latest_commit_sha":null,"homepage":"https://apps.amandaghassaei.com/gpu-io/examples/","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/amandaghassaei.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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}},"created_at":"2020-11-03T23:18:12.000Z","updated_at":"2025-05-07T09:06:26.000Z","dependencies_parsed_at":"2024-01-11T23:22:30.882Z","dependency_job_id":"8f435b32-fcda-48b2-8bb3-1224b62a745f","html_url":"https://github.com/amandaghassaei/gpu-io","commit_stats":{"total_commits":140,"total_committers":3,"mean_commits":"46.666666666666664","dds":"0.014285714285714235","last_synced_commit":"14a60817291f7ffaa68db531de647567ec321a18"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amandaghassaei%2Fgpu-io","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amandaghassaei%2Fgpu-io/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amandaghassaei%2Fgpu-io/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amandaghassaei%2Fgpu-io/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amandaghassaei","download_url":"https://codeload.github.com/amandaghassaei/gpu-io/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"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":["canvas","glsl","gpgpu","gpu","parallel","particles","physics","shader","shaders","simulation","threejs","webgl","webgl1","webgl2"],"created_at":"2024-08-01T21:02:17.284Z","updated_at":"2025-05-15T15:04:26.525Z","avatar_url":"https://github.com/amandaghassaei.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# gpu-io\n[![gpu-io main image](./docs/main-image.jpg)](https://apps.amandaghassaei.com/gpu-io/examples/)\n\n[![NPM Package](https://img.shields.io/npm/v/gpu-io)](https://www.npmjs.com/package/gpu-io)\n[![Build Size](https://img.shields.io/bundlephobia/min/gpu-io)](https://bundlephobia.com/result?p=gpu-io)\n[![NPM Downloads](https://img.shields.io/npm/dw/gpu-io)](https://www.npmtrends.com/gpu-io)\n[![License](https://img.shields.io/npm/l/gpu-io)](https://github.com/amandaghassaei/gpu-io/blob/main/LICENSE.txt)\n\n**A GPU-accelerated computing library for running physics simulations on the web**\n\ngpu-io is a WebGL library that helps you easily compose GPU-accelerated computing workflows.  This library can be used for a variety of applications including real-time physics simulations, particle/agent-based simulations, cellular automata, image processing, and general purpose GPU computations.  gpu-io supports rendering directly to the WebGL canvas and has some built-in features that make interactivity easy.  See [Examples](https://apps.amandaghassaei.com/gpu-io/examples/) for more details.\n\nDesigned for WebGL 2.0 (if available), with fallbacks to support WebGL 1.0 - so it should run on practically any mobile or older browsers.  WebGPU support is planned in the future.\n\n\n### Motivation\n\nThe main motivation behind gpu-io is to make it easier to compose GPU-accelerated applications without worrying too much about low-level WebGL details.  This library manages WebGL state, implements shader and program caching, and deals with issues of available WebGL versions or spec inconsistencies across different browsers/hardware.  It should significantly cut down on the amount of boilerplate code and state management you need to do in your applications.  At the same time, gpu-io gives you enough low-level control to write extremely efficient programs for computationally demanding applications.\n\n[As of Feb 2022, WebGL2 has now been rolled out to all major platforms](https://www.khronos.org/blog/webgl-2-achieves-pervasive-support-from-all-major-web-browsers) (including mobile Safari and Microsoft Edge) - but even among WebGL2 implementations, there are differences in behavior across browsers (especially mobile).  Additionally, you may still come across non-WebGL2 enabled browsers in the wild for some time.  gpu-io rigorously checks for these gotchas and uses software polyfills to patch any issues so you don't have to worry about it.  gpu-io will also attempt to automatically [convert your GLSL3 shader code into GLSL1](https://github.com/amandaghassaei/gpu-io/blob/main/docs/GLSL1_Support.md) so that it can run in WebGL1 in a pinch. See [tests/README.md](https://github.com/amandaghassaei/gpu-io/tree/main/tests#browser-support) for more information on browser support.\n\n- [Installation](#installation)\n- [Use](#use)\n- [Examples](#examples)\n- [API](#api)\n- [Compatibility with Threejs](#compatibility-with-threejs)\n- [Limitations/Notes](#limitationsnotes)\n- [Acknowledgements](#acknowledgements)\n- [License](#license)\n- [Development](#development)\n\n\n## Installation\n\n### Install via npm\n\n`npm install gpu-io`\n\nAnd import into your project:\n\n```js\nimport { GPUComposer, GPULayer, GPUProgram } from 'gpu-io';\n```\n\n\n### Import into HTML\n\n*OR* you can add [gpu-io.js](https://raw.githubusercontent.com/amandaghassaei/gpu-io/main/dist/gpu-io.js) or [gpu-io.min.js](https://raw.githubusercontent.com/amandaghassaei/gpu-io/main/dist/gpu-io.min.js) to your html directly:\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cscript src=\"gpu-io.js\"\u003e\u003c/script\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nGPUIO will be accessible globally:\n\n```js\nconst { GPUComposer, GPULayer, GPUProgram } = GPUIO;\n```\n\n\n## Use\n\nIf you have questions about how to use gpu-io: feel free to start a new [discussion thread](https://github.com/amandaghassaei/gpu-io/discussions).\n\nA simple example of how to use gpu-io to simulate 2D diffusion:\n\n```js\nimport {\n  GPUComposer,\n  GPULayer,\n  GPUProgram,\n  renderAmplitudeProgram,\n  FLOAT,\n  INT,\n  REPEAT,\n  NEAREST,\n} from 'gpu-io';\n\n// Init a canvas element.\nconst canvas = document.createElement('canvas');\ndocument.body.appendChild(canvas);\n\n// Init a composer.\nconst composer = new GPUComposer({ canvas });\n\n// Init a layer of float data filled with noise.\nconst noise = new Float32Array(canvas.width * canvas.height);\nnoise.forEach((el, i) =\u003e noise[i] = Math.random());\nconst state = new GPULayer(composer, {\n  name: 'state',\n  dimensions: [canvas.width, canvas.height],\n  numComponents: 1, // Scalar state has one component.\n  type: FLOAT,\n  filter: NEAREST,\n  // Use 2 buffers so we can toggle read/write\n  // from one to the other.\n  numBuffers: 2,\n  wrapX: REPEAT,\n  wrapY: REPEAT,\n  array: noise,\n});\n\n// Init a program to diffuse state.\nconst diffuseProgram = new GPUProgram(composer, {\n  name: 'render',\n  fragmentShader: `\n    in vec2 v_uv;\n\n    uniform sampler2D u_state;\n    uniform vec2 u_pxSize;\n\n    out float out_result;\n\n    void main() {\n      // Compute the discrete Laplacian.\n      // https://en.wikipedia.org/wiki/Discrete_Laplace_operator\n      float center = texture(u_state, v_uv).x;\n      float n = texture(u_state, v_uv + vec2(0, u_pxSize.y)).x;\n      float s = texture(u_state, v_uv - vec2(0, u_pxSize.y)).x;\n      float e = texture(u_state, v_uv + vec2(u_pxSize.x, 0)).x;\n      float w = texture(u_state, v_uv - vec2(u_pxSize.x, 0)).x;\n      const float diffusionRate = 0.1;\n      out_result =\n        center + diffusionRate * (n + s + e + w - 4.0 * center);\n    }\n  `,\n  uniforms: [\n    { // Index of sampler2D uniform to assign to value \"u_state\".\n      name: 'u_state',\n      value: 0,\n      type: INT,\n    },\n    { // Calculate the size of a 1 px step in UV coordinates.\n      name: 'u_pxSize',\n      value: [1 / canvas.width, 1 / canvas.height],\n      type: FLOAT,\n    },\n  ],\n});\n\n// Init a program to render state to canvas.\n// See https://github.com/amandaghassaei/gpu-io/tree/main/docs#gpuprogram-helper-functions\n// for more built-in GPUPrograms to use.\nconst renderProgram = renderAmplitudeProgram(composer, {\n  name: 'render',\n  type: state.type,\n  components: 'x',\n});\n\n// Simulation/render loop.\nfunction loop() {\n  window.requestAnimationFrame(loop);\n\n  // Diffuse state and write result to state.\n  composer.step({\n    program: diffuseProgram,\n    input: state,\n    output: state,\n  });\n\n  // If no \"output\", will draw to canvas.\n  composer.step({\n    program: renderProgram,\n    input: state,\n  });\n}\nloop(); // Start animation loop.\n```\n\n[Demo this code](https://apps.amandaghassaei.com/gpu-io/examples/demo/) - You should see the noise slowly blur, refresh the page to start it over.\n\n\n## Examples\n\nCheck out the [Examples page](https://apps.amandaghassaei.com/gpu-io/examples/) to really understand how gpu-io works and how to easily create touch interactions in your application.\nSource code for all examples can be found in the [examples/](https://github.com/amandaghassaei/gpu-io/tree/main/examples) folder.\n\nPlease [let me know](https://amandaghassaei.com/about/) if you made something with gpu-io!  Feel free to also post a link in the [Show and Tell](https://github.com/amandaghassaei/gpu-io/discussions/5) discussions thread.  I'll periodically add some of these to the Examples page as well.\n\n\n## API\n\nFull API documentation can be found in the [docs/](https://github.com/amandaghassaei/gpu-io/tree/main/docs) folder.\n\nMore information about writing GLSL shaders for gpu-io can be found at [docs/GLSL](https://github.com/amandaghassaei/gpu-io/blob/main/docs/GLSL.md).\n\n\n## Compatibility with Threejs\n\ngpu-io can share a webgl context with Threejs so that both libraries will be able to access shared memory on the gpu:\n\n```js\nimport THREE from 'three';\nimport {\n  GPUComposer,\n  GPULayer,\n  FLOAT,\n  CLAMP_TO_EDGE,\n  LINEAR,\n} from 'gpu-io';\n\nconst renderer = new THREE.WebGLRenderer();\n// Use renderer.autoClear = false if you want to overlay threejs stuff\n// on top of things rendered to the screen from gpu-io.\n// renderer.autoClear = false;\n\nconst composer = GPUComposer.initWithThreeRenderer(renderer);\n```\n\nData is passed between gpu-io and Threejs via WebGLTextures.  To bind a GPULayer to a Threejs Texture:\n\n```js\nconst layer1 = new GPULayer(composer, {\n  name: 'layer1',\n  dimensions: [100, 100],\n  type: FLOAT,\n  numComponents: 1,\n});\n\nconst texture = new THREE.Texture();\n// Link webgl texture to threejs object.\nlayer1.attachToThreeTexture(texture);\n\n// Use texture in threejs scene.\nconst mesh = new THREE.Mesh(\n  new PlaneBufferGeometry(1, 1),\n  new MeshBasicMaterial({\n    map: texture,\n  }),\n);\n\nloop() {\n  // Undo any changes threejs has made to global WebGL state.\n  composer.undoThreeState();\n\n  // Compute things with gpu-io.\n  composer.step({\n    program: myProgram,\n    output: layer1,\n  });\n\n  ....\n\n  // Reset global WebGL state back to what threejs is expecting\n  // (otherwise we get WebGL errors).\n  composer.resetThreeState();\n  // Render threejs scene.\n  // Updates to layer1 will propagate to texture without any\n  // additional needsUpdate flags.\n  renderer.render(scene, camera);\n}\n```\n\nMore info about using gpu-io with Threejs can be found in the [Threejs Example](https://github.com/amandaghassaei/gpu-io/tree/main/examples/wave2d).\n\n\n## Limitations/Notes\n\n\n### Limitations\n\n- gpu-io currently only supports GPULayers with 1D or 2D arrays of dense data.  3D textures are not officially supported by the library (e.g. there are not currently 3D GPULayers, though I am [looking at adding this](https://github.com/amandaghassaei/gpu-io/issues/8)).  If you want to compute 3D simulations on triangle meshes in gpu-io, you will just need to pass in your 3D position data as a 1D list to a GPULayer and then access it in the fragment shader using .xyz.  TODO: make example for this.\n- gpu-io does not currently allow you to pass in your own vertex shaders.  Currently all computation is happening in user-specified fragment shaders; vertex shaders are managed internally.\n- In order for the WRAP/FILTER polyfilling to work correctly, any calls to texture() must contain a direct reference to the sampler2D that it should operate on.  For example:\n\n```glsl\nvarying vec2 v_uv;\n\nuniform sampler2D u_sampler1;\nuniform sampler2D u_sampler2;\n\nout vec4 out_result;\n\nvec4 lookupSampler2(vec2 uv) {\n  // This is good, it passes u_sampler2 directly to texture().\n  return texture(u_sampler2, uv);\n}\n\nvec4 lookupSampler(sampler2D sampler, vec2 uv) {\n  // At compile time it is hard to say which sampler\n  // is passed to texture().\n  // This will not be polyfilled, it will throw a warning.\n  return texture(sampler, uv);\n}\n\nvoid main() {\n  // This is good, it passes u_sampler1 directly to texture().\n  vec2 position = texture(u_sampler1, v_uv).xy;\n\n  ....\n}\n```\n\n\n### GLSL Version\n\ngpu-io defaults to using WebGL2 (if available) with GLSL version 300 (GLSL3) but you can set it to use WebGL1 or GLSL version 100 (GLSL1) by passing `contextID` or `glslVersion` parameters to `GPUComposer`:\n\n```js\nimport {\n  GPUComposer,\n  GLSL1,\n  WEBGL1,\n} from 'gpu-io';\n\n// Init with WebGL2 (if available) with GLSL1.\nconst composer1 = new GPUComposer({\n  canvas: document.createElement('canvas'),\n  glslVersion: GLSL1,\n});\n\n// Init with WebGL1 with GLSL1 (GLSL3 is not supported in WebGL1).\nconst composer2 = new GPUComposer({\n  canvas: document.createElement('canvas'),\n  contextID: WEBGL1,\n});\n```\n\nSee [docs\u003eGPUComposer\u003econstructor](https://github.com/amandaghassaei/gpu-io/blob/main/docs/classes/GPUComposer.md#constructor) for more information.\n\ngpu-io will automatically convert any GLSL3 shaders to GLSL1 when targeting WebGL1.  If supporting WebGL1/GLSL1 is important to you, see the [GLSL1 Support](https://github.com/amandaghassaei/gpu-io/blob/main/docs/GLSL1_Support.md) doc for more info about what functions/types/operators are available in gpu-io's flavor of GLSL1.\n\n\n### Transform Feedback\n\nYou might notice that gpu-io does not use any transform feedback to handle computations on GPULayers.  Transform feedback is great for things like particle simulations and other types of physics that is computed on the vertex level as opposed to the pixel level.  It is still absolutely possible to perform these types of simulations using gpu-io (see [Examples](https://apps.amandaghassaei.com/gpu-io/examples/)), but currently all the computation happens in a fragment shader.  There are a few reasons for this:\n\n- The main use case for gpu-io is to operate on 2D spatially-distributed state (i.e. fields) stored in textures using fragment shaders.  There is additional support for 1D arrays and lines/particles, but that is secondary functionality.\n- Transform feedback is only supported in WebGL2.  At the time I first started writing this in 2020, WebGL2 was not supported by mobile Safari.  Though that has changed recently, for now I'd like to support all functionality in gpu-io in WebGL1/GLSL1 as well.\n- The API is simpler if we constrain computations to the fragment shader only.\n\nMy current plan is to wait for [WebGPU](https://web.dev/gpu/) to officially launch by default in some browsers, and then re-evaluate some of the design decisions made in gpu-io.  WebGL puts artificial constraints on the current API by forcing general-purpose computing to happen in a vertex and fragment shader rendering pipeline rather than a compute pipeline, so I'd like to get away from WebGL in the long term – and using transform feedback feels like a step backwards at this point.\n\n\n### Precision\n\nBy default all shaders in gpu-io are inited with highp precision floats and ints, but they will fall back to mediump if highp is not available (this is the same convention used by Threejs).  More info in [src/glsl/common/precision.ts](https://github.com/amandaghassaei/gpu-io/blob/main/src/glsl/common/precision.ts).\n\nYou can override these defaults by specifying `intPrecision` and `floatPrecision` in GPUComposer's constructor:\n```js\nimport {\n  GPUComposer,\n  PRECISION_LOW_P,\n  PRECISION_MEDIUM_P,\n  PRECISION_HIGH_P,\n} from 'gpu-io';\n\nconst composer = new GPUComposer({\n  canvas: document.getElementById('webgl-canvas'),\n  intPrecision: PRECISION_MEDIUM_P,\n  floatPrecision: PRECISION_MEDIUM_P,\n});\n```\n\nOf course, you can also always manually specify the precision of a particular variable in your shader code:\n\n```glsl\nin vec2 v_uv;\n\n// u_state is a BYTE array, so we can set its precision to lowp.\nuniform lowp isampler2D u_state;\n\nout vec4 out_result;\n\nvoid main() {\n  lowp int state = texture(u_state, v_uv).r;\n  ....\n}\n```\n\n**Note: even if highp is specified in your shader code, gpu-io will convert to mediump if the current browser does not support highp (the alternative would be to throw an error).**\n\nI've also included the following helper functions to test the precision of mediump on your device and determine whether highp is supported:\n\n```js\nimport {\n  isHighpSupportedInVertexShader,\n  isHighpSupportedInFragmentShader,\n  getVertexShaderMediumpPrecision,\n  getFragmentShaderMediumpPrecision,\n} from 'gpu-io';\n\n// Prints 'highp' or 'mediump' depending on returned precision of\n// mediump (16+bit or 32+bit).\n// On many devices (esp desktop) mediump defaults to 32bit.\n// See https://webglfundamentals.org/webgl/lessons/webgl-precision-issues.html\n// for more info.\nconsole.log(getVertexShaderMediumpPrecision());\nconsole.log(getFragmentShaderMediumpPrecision());\n\n// Print true or false depending on highp support of browser/device.\nconsole.log(isHighpSupportedInVertexShader());\nconsole.log(isHighpSupportedInFragmentShader());\n```\n\n\n## Acknowledgements\n\nI used a few codebases as reference when writing this, thanks to their authors for making these repos available:\n\n- [three.js](https://github.com/mrdoob/three.js/)\n- [regl](https://github.com/regl-project/regl)\n- [gpu.js](https://github.com/gpujs/gpu.js/)\n- [WebGL Boilerplate](https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html)\n- [GPU Accelerated Particles with WebGL 2](https://gpfault.net/posts/webgl2-particles.txt.html)\n\nOther resources:\n\n- [WebGL best practices](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices) by Mozilla\n- [Compatibility issues in Shadertoy / webGLSL](https://shadertoyunofficial.wordpress.com/2016/07/22/compatibility-issues-in-shadertoy-webglsl/) Shadertoy – Unofficial\n\n\n## License\n\nThis work is distributed under an [MIT license](https://github.com/amandaghassaei/gpu-io/blob/main/LICENSE.txt).  Note that gpu-io depends on a few npm packages:\n\n- [@amandaghassaei/type-checks](https://www.npmjs.com/package/@amandaghassaei/type-checks) - MIT license, no dependencies.\n- [@petamoriken/float16](https://www.npmjs.com/package/@petamoriken/float16) - MIT license, no dependencies.\n- [changedpi](https://www.npmjs.com/package/changedpi) - MIT license, no dependencies.\n- [file-saver](https://www.npmjs.com/package/file-saver) - MIT license, no dependencies.\n\n\n## Development\n\nUpdate 10/2022:  I'm switching gears a bit to focus on some new projects, but I'll be continuing to use gpu-io as the foundation for almost everything I'm working on.  I expect that some new features will be added to this over the next six months or so, but I can't guarantee I'll have time to help debug issues you may run into.  Feel free to log [issues](https://github.com/amandaghassaei/gpu-io/issues) or ask [questions](https://github.com/amandaghassaei/gpu-io/discussions), but don't expect a super prompt response! See the [Examples](https://apps.amandaghassaei.com/gpu-io/examples/) for more info about how to use this library.\n\nPull requests welcome! I hope this library is useful to others, but I also realize that I have some very specific needs that have influenced the direction of this code – so we'll see what happens.  Please [let me know](https://amandaghassaei.com/about/) if you end up using this, I'd love to see what you're making!  \n\n\n### Compiling with Webpack\n\nCompiled with [webpack](https://www.npmjs.com/package/webpack).  To build ts files from `src` to js in `dist` run:\n\n```\nnpm install\nnpm run build\n```\n\n\n### Automated Testing\n\nI'm using mocha + karma + chai + headless Chrome to test the components of gpu-io, following the setup described in [Automated testing with Headless Chrome](https://developer.chrome.com/blog/headless-karma-mocha-chai/).  Those tests are located in [tests/mocha/](https://github.com/amandaghassaei/gpu-io/blob/main/tests/mocha/).  To run the automated tests, use:\n\n```\nnpm run test\n```\n\nThe automated tests do not get full code coverage yet, but I'm planning to add to them when I go back to implement WebGPU features in this library.\n\n### Browser/Device Testing\n\nI've also included a webpage for testing various functions of this library in a browser/hardware combo of your choice.  This page is current hosted at [apps.amandaghassaei.com/gpu-io/tests/](https://apps.amandaghassaei.com/gpu-io/tests/).\n\nNote: The detected OS and browser version may not always be 100% accurate.\n\nSee [tests/README#browser-support](https://github.com/amandaghassaei/gpu-io/blob/main/tests/README.md#browser-support) for results of various browser/hardware combos.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famandaghassaei%2Fgpu-io","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famandaghassaei%2Fgpu-io","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famandaghassaei%2Fgpu-io/lists"}