WebGPU Metaballs example Simple 2d shader with metaballs charges distances Maybe mix it with the smooth particle hydrodynamics SPH demo splashy water effect https notebook xbdev net index php page webgpu9 var script document createElement script script type text javascript script async false script src https cdnjs cloudflare com ajax libs gl matrix 2 6 0 gl matrix min js document head appendChild script const canvas document createElement canvas document body appendChild canvas canvas width canvas height 512 console log canvas width canvas height const context canvas getContext webgpu const gpu navigator gpu console log gpu gpu const adapter await gpu requestAdapter const device await adapter requestDevice const devicePixelRatio window devicePixelRatio 1 const presentationSize canvas clientWidth devicePixelRatio canvas clientHeight devicePixelRatio const presentationFormat context getPreferredFormat adapter console log presentationFormat context configure device device format presentationFormat size presentationSize var vertWGSL struct VSOut builtin position Position vec4 f32 location 0 pos vec3 f32 location 1 uvs vec2 f32 stage vertex fn main builtin vertex_index VertexIndex u32 VSOut var s 0 9 var pos array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s s 1 0 var uvs array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s var vsOut VSOut vsOut Position vec4 f32 pos VertexIndex 0 0 1 0 vsOut pos vec3 f32 pos VertexIndex 0 0 vsOut uvs uvs VertexIndex 0 5 0 5 0 1 return vsOut var fragWGSL block struct Uniforms2 instancePos stride 16 array vec4 f32 64 group 0 binding 0 var mySampler sampler group 0 binding 1 var myTexture texture_2d f32 group 0 binding 2 var uniform uniforms2 Uniforms2 stage fragment fn main location 0 pos vec3 f32 location 1 uvs vec2 f32 location 0 vec4 f32 var numBalls 8 var charge 0 0 for var i 0 i numBalls i i 1 var dist distance pos xy uniforms2 instancePos i xy if abs dist 0 0001 dist 0 0001 charge charge 1 0 dist dist charge charge 100 0 if charge 0 4 return vec4 f32 charge 2 0 charge 2 0 charge 1 0 return vec4 f32 1 0 1 0 0 0 1 0 let textureSampler device createSampler minFilter linear magFilter linear const img document createElement img img src https notebook xbdev net var images test512 png await img decode const basicTexture device createTexture size img width img height 1 format presentationFormat bgra8unorm rgba8unorm usage 0x2 0x4 GPUTextureUsage COPY_DST GPUTextureUsage TEXTURE_BINDING const imageCanvas document createElement canvas imageCanvas width img width imageCanvas height img height const imageCanvasContext imageCanvas getContext 2d imageCanvasContext drawImage img 0 0 imageCanvas width imageCanvas height const imageData imageCanvasContext getImageData 0 0 imageCanvas width imageCanvas height let textureData new Uint8Array img width img height 4 for let x 0 x img width img height 4 x textureData x imageData data x device queue writeTexture texture basicTexture textureData offset 0 bytesPerRow img width 4 rowsPerImage img height img width img height 1 let numInstances 8 let vel let instanceData new Float32Array 4 numInstances 4 2 8 8 3 for let i 0 i instanceData length 4 i instanceData i 4 0 1 Math random 1 0 instanceData i 4 1 1 Math random 1 0 instanceData i 4 2 0 vel i x Math random 0 5 1 0 1 0 y Math random 0 5 1 0 1 0 z 0 const instancesUniformBuffer device createBuffer size instanceData byteLength 4 3 numInstances 128 isntances of vec3 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength const timeUniformBuffer device createBuffer size 4 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST const sceneUniformBindGroupLayout device createBindGroupLayout entries binding 0 visibility GPUShaderStage FRAGMENT sampler type filtering binding 1 visibility GPUShaderStage FRAGMENT texture sampleType float viewDimension 2d binding 2 visibility GPUShaderStage FRAGMENT buffer type uniform const uniformBindGroup device createBindGroup layout sceneUniformBindGroupLayout entries binding 0 resource textureSampler binding 1 resource basicTexture createView binding 2 resource buffer instancesUniformBuffer const pipeline device createRenderPipeline layout device createPipelineLayout bindGroupLayouts sceneUniformBindGroupLayout vertex module device createShaderModule code vertWGSL entryPoint main fragment module device createShaderModule code fragWGSL entryPoint main targets format presentationFormat primitive topology triangle strip frontFace ccw cullMode back stripIndexFormat uint32 GPURenderPassDescriptor const renderPassDescriptor colorAttachments view undefined asign later in frame loadValue r 0 0 g 0 5 b 0 5 a 1 0 storeOp store function frame animate the metaballs by slowly moving them around for let i 0 i instanceData length 4 i let x instanceData i 4 0 let y instanceData i 4 1 x vel i x 0 005 y vel i y 0 005 if x 1 vel i x 1 x 1 if x 1 vel i x 1 x 1 if y 1 vel i y 1 y 1 if y 1 vel i y 1 y 1 instanceData i 4 0 x instanceData i 4 1 y device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength renderPassDescriptor colorAttachments 0 view context getCurrentTexture createView const commandEncoder device createCommandEncoder const renderPass commandEncoder beginRenderPass renderPassDescriptor renderPass setPipeline pipeline renderPass setBindGroup 0 uniformBindGroup renderPass draw 4 1 0 0 renderPass endPass device queue submit commandEncoder finish if you want constant updates animated keep refreshing requestAnimationFrame frame frame console log ready WebGPU Metaballs example Extend the concept a bit by calculating a normal based on the surface charge var script document createElement script script type text javascript script async false script src https cdnjs cloudflare com ajax libs gl matrix 2 6 0 gl matrix min js document head appendChild script const canvas document createElement canvas document body appendChild canvas canvas width canvas height 512 console log canvas width canvas height const context canvas getContext webgpu const gpu navigator gpu console log gpu gpu const adapter await gpu requestAdapter const device await adapter requestDevice const devicePixelRatio window devicePixelRatio 1 const presentationSize canvas clientWidth devicePixelRatio canvas clientHeight devicePixelRatio const presentationFormat context getPreferredFormat adapter console log presentationFormat context configure device device format presentationFormat size presentationSize var vertWGSL struct VSOut builtin position Position vec4 f32 location 0 pos vec3 f32 location 1 uvs vec2 f32 stage vertex fn main builtin vertex_index VertexIndex u32 VSOut var s 0 9 var pos array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s s 1 0 var uvs array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s var vsOut VSOut vsOut Position vec4 f32 pos VertexIndex 0 0 1 0 vsOut pos vec3 f32 pos VertexIndex 0 0 vsOut uvs uvs VertexIndex 0 5 0 5 0 1 return vsOut var fragWGSL block struct Uniforms2 instancePos stride 16 array vec4 f32 64 group 0 binding 0 var mySampler sampler group 0 binding 1 var myTexture texture_2d f32 group 0 binding 2 var uniform uniforms2 Uniforms2 fn getCharge cpos vec2 f32 f32 var numBalls 8 var charge 0 0 for var i 0 i numBalls i i 1 var dist distance cpos xy uniforms2 instancePos i xy if abs dist 0 0001 dist 0 0001 charge charge 1 0 dist dist charge charge 100 0 return charge stage fragment fn main location 0 pos vec3 f32 location 1 uvs vec2 f32 location 0 vec4 f32 var c0 getCharge pos xy var c1 getCharge pos xy vec2 f32 0 01 0 0 var c2 getCharge pos xy vec2 f32 0 0 0 01 if c0 0 4 var v0 vec3 f32 c1 0 0 0 0 var v1 vec3 f32 0 0 c2 0 0 var n normalize cross v0 v1 var n normalize vec3 f32 c0 c1 c0 c2 0 0 return vec4 f32 n x n y n z 1 0 var lightdir normalize vec3 f32 0 0 0 3 0 7 var illum dot lightdir n return vec4 f32 illum illum illum 1 0 return vec4 f32 1 0 1 0 0 0 1 0 let textureSampler device createSampler minFilter linear magFilter linear const img document createElement img img src https notebook xbdev net var images test512 png await img decode const basicTexture device createTexture size img width img height 1 format presentationFormat bgra8unorm rgba8unorm usage 0x2 0x4 GPUTextureUsage COPY_DST GPUTextureUsage TEXTURE_BINDING const imageCanvas document createElement canvas imageCanvas width img width imageCanvas height img height const imageCanvasContext imageCanvas getContext 2d imageCanvasContext drawImage img 0 0 imageCanvas width imageCanvas height const imageData imageCanvasContext getImageData 0 0 imageCanvas width imageCanvas height let textureData new Uint8Array img width img height 4 for let x 0 x img width img height 4 x textureData x imageData data x device queue writeTexture texture basicTexture textureData offset 0 bytesPerRow img width 4 rowsPerImage img height img width img height 1 let numInstances 8 let vel let instanceData new Float32Array 4 numInstances 4 2 8 8 3 for let i 0 i instanceData length 4 i instanceData i 4 0 1 Math random 1 0 instanceData i 4 1 1 Math random 1 0 instanceData i 4 2 0 vel i x Math random 0 5 1 0 1 0 y Math random 0 5 1 0 1 0 z 0 const instancesUniformBuffer device createBuffer size instanceData byteLength 4 3 numInstances 128 isntances of vec3 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength const timeUniformBuffer device createBuffer size 4 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST const sceneUniformBindGroupLayout device createBindGroupLayout entries binding 0 visibility GPUShaderStage FRAGMENT sampler type filtering binding 1 visibility GPUShaderStage FRAGMENT texture sampleType float viewDimension 2d binding 2 visibility GPUShaderStage FRAGMENT buffer type uniform const uniformBindGroup device createBindGroup layout sceneUniformBindGroupLayout entries binding 0 resource textureSampler binding 1 resource basicTexture createView binding 2 resource buffer instancesUniformBuffer const pipeline device createRenderPipeline layout device createPipelineLayout bindGroupLayouts sceneUniformBindGroupLayout vertex module device createShaderModule code vertWGSL entryPoint main fragment module device createShaderModule code fragWGSL entryPoint main targets format presentationFormat primitive topology triangle strip frontFace ccw cullMode back stripIndexFormat uint32 GPURenderPassDescriptor const renderPassDescriptor colorAttachments view undefined asign later in frame loadValue r 0 0 g 0 5 b 0 5 a 1 0 storeOp store function frame animate the metaballs by slowly moving them around for let i 0 i instanceData length 4 i let x instanceData i 4 0 let y instanceData i 4 1 x vel i x 0 005 y vel i y 0 005 if x 1 vel i x 1 x 1 if x 1 vel i x 1 x 1 if y 1 vel i y 1 y 1 if y 1 vel i y 1 y 1 instanceData i 4 0 x instanceData i 4 1 y device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength renderPassDescriptor colorAttachments 0 view context getCurrentTexture createView const commandEncoder device createCommandEncoder const renderPass commandEncoder beginRenderPass renderPassDescriptor renderPass setPipeline pipeline renderPass setBindGroup 0 uniformBindGroup renderPass draw 4 1 0 0 renderPass endPass device queue submit commandEncoder finish if you want constant updates animated keep refreshing requestAnimationFrame frame frame console log ready
script src https cdnjs cloudflare com ajax libs gl matrix 2 6 0 gl matrix min js document head appendChild script const canvas document createElement canvas document body appendChild canvas canvas width canvas height 512 console log canvas width canvas height const context canvas getContext webgpu const gpu navigator gpu console log gpu gpu const adapter await gpu requestAdapter const device await adapter requestDevice const devicePixelRatio window devicePixelRatio 1 const presentationSize canvas clientWidth devicePixelRatio canvas clientHeight devicePixelRatio const presentationFormat context getPreferredFormat adapter console log presentationFormat context configure device device format presentationFormat size presentationSize var vertWGSL struct VSOut builtin position Position vec4 f32 location 0 pos vec3 f32 location 1 uvs vec2 f32 stage vertex fn main builtin vertex_index VertexIndex u32 VSOut var s 0 9 var pos array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s s 1 0 var uvs array vec2 f32 4 vec2 f32 s s vec2 f32 s s vec2 f32 s s vec2 f32 s s var vsOut VSOut vsOut Position vec4 f32 pos VertexIndex 0 0 1 0 vsOut pos vec3 f32 pos VertexIndex 0 0 vsOut uvs uvs VertexIndex 0 5 0 5 0 1 return vsOut var fragWGSL block struct Uniforms2 instancePos stride 16 array vec4 f32 64 group 0 binding 0 var mySampler sampler group 0 binding 1 var myTexture texture_2d f32 group 0 binding 2 var uniform uniforms2 Uniforms2 fn getCharge cpos vec2 f32 f32 var numBalls 8 var charge 0 0 for var i 0 i numBalls i i 1 var dist distance cpos xy uniforms2 instancePos i xy if abs dist 0 0001 dist 0 0001 charge charge 1 0 dist dist charge charge 100 0 return charge stage fragment fn main location 0 pos vec3 f32 location 1 uvs vec2 f32 location 0 vec4 f32 var c0 getCharge pos xy var c1 getCharge pos xy vec2 f32 0 01 0 0 var c2 getCharge pos xy vec2 f32 0 0 0 01 if c0 0 4 var v0 vec3 f32 c1 0 0 0 0 var v1 vec3 f32 0 0 c2 0 0 var n normalize cross v0 v1 var n normalize vec3 f32 c0 c1 c0 c2 0 0 return vec4 f32 n x n y n z 1 0 var lightdir normalize vec3 f32 0 0 0 3 0 7 var illum dot lightdir n return vec4 f32 illum illum illum 1 0 return vec4 f32 1 0 1 0 0 0 1 0 let textureSampler device createSampler minFilter linear magFilter linear const img document createElement img img src https notebook xbdev net var images test512 png await img decode const basicTexture device createTexture size img width img height 1 format presentationFormat bgra8unorm rgba8unorm usage 0x2 0x4 GPUTextureUsage COPY_DST GPUTextureUsage TEXTURE_BINDING const imageCanvas document createElement canvas imageCanvas width img width imageCanvas height img height const imageCanvasContext imageCanvas getContext 2d imageCanvasContext drawImage img 0 0 imageCanvas width imageCanvas height const imageData imageCanvasContext getImageData 0 0 imageCanvas width imageCanvas height let textureData new Uint8Array img width img height 4 for let x 0 x img width img height 4 x textureData x imageData data x device queue writeTexture texture basicTexture textureData offset 0 bytesPerRow img width 4 rowsPerImage img height img width img height 1 let numInstances 8 let vel let instanceData new Float32Array 4 numInstances 4 2 8 8 3 for let i 0 i instanceData length 4 i instanceData i 4 0 1 Math random 1 0 instanceData i 4 1 1 Math random 1 0 instanceData i 4 2 0 vel i x Math random 0 5 1 0 1 0 y Math random 0 5 1 0 1 0 z 0 const instancesUniformBuffer device createBuffer size instanceData byteLength 4 3 numInstances 128 isntances of vec3 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength const timeUniformBuffer device createBuffer size 4 usage GPUBufferUsage UNIFORM GPUBufferUsage COPY_DST const sceneUniformBindGroupLayout device createBindGroupLayout entries binding 0 visibility GPUShaderStage FRAGMENT sampler type filtering binding 1 visibility GPUShaderStage FRAGMENT texture sampleType float viewDimension 2d binding 2 visibility GPUShaderStage FRAGMENT buffer type uniform const uniformBindGroup device createBindGroup layout sceneUniformBindGroupLayout entries binding 0 resource textureSampler binding 1 resource basicTexture createView binding 2 resource buffer instancesUniformBuffer const pipeline device createRenderPipeline layout device createPipelineLayout bindGroupLayouts sceneUniformBindGroupLayout vertex module device createShaderModule code vertWGSL entryPoint main fragment module device createShaderModule code fragWGSL entryPoint main targets format presentationFormat primitive topology triangle strip frontFace ccw cullMode back stripIndexFormat uint32 GPURenderPassDescriptor const renderPassDescriptor colorAttachments view undefined asign later in frame loadValue r 0 0 g 0 5 b 0 5 a 1 0 storeOp store function frame animate the metaballs by slowly moving them around for let i 0 i instanceData length 4 i let x instanceData i 4 0 let y instanceData i 4 1 x vel i x 0 005 y vel i y 0 005 if x 1 vel i x 1 x 1 if x 1 vel i x 1 x 1 if y 1 vel i y 1 y 1 if y 1 vel i y 1 y 1 instanceData i 4 0 x instanceData i 4 1 y device queue writeBuffer instancesUniformBuffer 0 instanceData buffer instanceData byteOffet instanceData byteLength renderPassDescriptor colorAttachments 0 view context getCurrentTexture createView const commandEncoder device createCommandEncoder const renderPass commandEncoder beginRenderPass renderPassDescriptor renderPass setPipeline pipeline renderPass setBindGroup 0 uniformBindGroup renderPass draw 4 1 0 0 renderPass endPass device queue submit commandEncoder finish if you want constant updates animated keep refreshing requestAnimationFrame frame frame console log ready