www.xbdev.net
xbdev - software development
Wednesday May 7, 2025
Home | Contact | Support | WebGPU Graphics and Compute ...
     
 

WebGPU/WGSL Tutorials and Articles

Graphics and Compute ...

 



Texture worms - organic lifelike pattern generation.
Texture worms - organic lifelike pattern generation.


Wiggly Worms (Texture Pattern Worms)


The example is candy for the eyes - as you can watch worms move around the screen - leaving a trail behind them. As they move around, they'll try and avoid other worm trails.

Using a simple avoid logic that the worms turn right, but when they hit something (wall) they iteratively turn left trying to avoid the collision.

What you end up with is lots of worms traveling around while generating a hypnotic pattern.



Texture worm pattern as it evolves over time - showing the pattern creation.
Texture worm pattern as it evolves over time - showing the pattern creation.


Algorithm (How it Works)


Give you the keys points

• Implemented on the GPU using WebGPU
• Ping-pong textures (2 textures draw to one texture which is then passed to the next draw)
• RGBA32 format - so it avoids numerical limitations (create smoother patterns)
• Hacky bit - the worm data is stored in a single row of pixels at the top of the image (like Teletex - do you remember Teletex?)



The worms have a position/angle/age which is stored in a single rgba value - the first line of pixels (bit hacky this but it works).

Alternatively you could use the 'alpha' to track which pixels are 'head' and use them to move around (not overlapping or hitting any other heads).

For the default the worms are given a constant 'gradient' so they always curve - creating the nice swirly pattern - however, you can add in more randomness or motion - so they choose a new direction on collision or sometimes clocwise and someetimes straight.

Creates a 'Tron' like effect - or if you're interested in biology - looks like the cross section of something organic/celluar. Have you seen 'Tron'? Bikes?



Tron and the bike scene - classic (and epic) movie
Tron and the bike scene - classic (and epic) movie




You can play around with the default parameters, such as, the thickness, increment angle, number of worms and so on to create all sorts of different patterns. The algorithm/code is actually very simple - but the resulting patterns are really complex and interesting (sort of have a fractal-like infinite feel).


Examples by modifying few of the paramters.
Examples by modifying few of the paramters.



The complete fragment shader is shown below - the complete code including a working online version is given at the bottom in the resources links.

@group(0) @binding(0) var  mySamplersampler;
@
group(0) @binding(1) var  myTexturetexture_2d<f32>;
@
group(0) @binding(2) var <uniformmytimer f32;


const 
WORM_WIDTH:f32 1.5;
const 
WORM_SPACING:f32 1.0;
const 
WORM_CURVE:f32 0.5;
const 
NUM_WORMS:f32 100.;
const 
resolutionvec2<f32> = vec2<f32>(512.0512.0);

// using rgba32 - for 128bit texture - can't use 'sampler'
fn textureSample2(t:texture_2d<f32>, s:samplercoords:vec2<f32>) -> vec4<f32>
{
    return 
textureLoadmyTexturevec2<i32>( i32(coords.x*512.0), i32(coords.y*512.0) ), );
    
//return textureSample(myTexture, mySampler, coords / resolution);
}

fn 
mymod(x:f32y:f32) -> f32
{
    return ( 
floor(x/y) );
}

fn 
mysmoothstep(edge0f32edge1f32xf32) -> f32 {
    
let t clamp((edge0) / (edge1 edge0), 0.01.0);
    return 
* (3.0 2.0 t);
}

fn 
calculateCosSin(angle:f32) -> vec2<f32>
{
    return 
vec2<f32>(cos(angle), sin(angle)) * 1.0;
}

fn 
randomValue(seed:f32) -> f32
{
    return 
2. fract(456.68 sin(1e3 seed mymod(1.0100.))) - 1.;
}

fn 
getTextureColor(coords:vec2<f32>) -> vec4<f32>
{
    return 
textureSample2(myTexturemySamplercoords resolution);
}

// Main fragment function
@fragment
fn main(@builtin(positionposition vec4<f32>) -> @location(0vec4<f32
{
    
let fragCoordvec2<f32> = position.xy;
    
let uv fragCoord * (1.0/512.0);
    
    var 
lastColor textureSample2(myTexturemySampleruv ).xyzw;
    var 
fragColor lastColor;

   if ( 
absfragCoord.0.5 ) < 1.0  && mytimer==0.0
   {
        var 
initialPosition:vec2<f32> = resolution 2.0 
                                        
resolution 2.4 
            
vec2(randomValue(fragCoord.x), 
                 
randomValue(fragCoord.0.1));

        var 
initialAngle:f32 3.14 randomValue(fragCoord.0.2);

        
fragColor vec4<f32>(initialPositioninitialAngle5.0);
    }



    for (var 
i:f32 0.0NUM_WORMS+= 1.0
    {
        var 
headState:vec4<f32> = getTextureColorvec2(i0.5) );

        if ( 
absfragCoord.0.5 ) > 1.0 && mytimer>0.0)
        {
            var 
distanceToHead:f32 length(headState.xy fragCoord);
            var 
color:vec4<f32> = .5 .5 sin(6.3 NUM_WORMS vec4(0, -2.12.11));

            
// hack to increase spacing using 'alpha' spacing
            
fragColor += mysmoothstep(WORM_WIDTH+WORM_SPACING0.0distanceToHead) * vec4<f32>(0,0,0,1);
            
            
fragColor += mysmoothstep(WORM_WIDTH0.0distanceToHead) * color;
        }
    }
    
      var 
headState:vec4<f32> = getTextureColor(fragCoord);  
  
    
    var 
angle:f32 headState.WORM_CURVE;
    var 
initialAngle angle;

    
let MAX_ITERATIONS:i32 25;
    
let TURN_ANGLE:f32 6.283185 f32(MAX_ITERATIONS);
    var 
found:bool false;
    for (var 
i:i32 0MAX_ITERATIONSi++) {
        
        var 
nextPosition:vec2<f32> = headState.xy + (WORM_WIDTH 2.0) * calculateCosSin(angle)*1.0;
        
        
let rgb lengthgetTextureColor(nextPosition).rgba );
        
        if ( 
rgb 0.1 || angle >= 13.0
        {
            
found true;
        }
        if ( !
found )
        {
            
angle += TURN_ANGLE;
        }
    }
        
    if ( !
found )
    {
        
angle headState.WORM_CURVE;
    }

    
    var 
cc headState.xy + (WORM_WIDTH 2.0) * calculateCosSin(angle)*1.0;
    var 
rgbTarget:f32 lengthgetTextureColorcc ).rgba );
    
    if ( 
absfragCoord.0.5 ) < 1.0 && mytimer>0.0 )
    {
        
// Add max .w if we want to limit the maximum age
        
if ( headState.0.0 //  && headState.w < 1000.0)
        
{
            
//if (rgbTarget < 0.1 )
            
{
                var 
newPosition:vec2<f32> = headState.xy calculateCosSin(angle)*1.0;
                var 
newAngle:f32 mymod(angle6.2832);
                var 
newTime:f32 headState.1.0;
                
                
fragColor vec4<f32>(newPositionnewAnglenewTime);
            }
        }
    }
    return 
fragColor;
}



Things to Try


• Modify the movement to create other patterns instead of just circular motions
• Give each worm a different speed
• Have some worms eat the trail of other worms!
• Make the worms have different thickness
• Gameify the concept - like Tron - you control one of the worms and you move around! Have to stay alive for a certain amount of time?
• Give the worms 'layers' - so they can only move and interact with worms on their own layer
• Limit the length of the worm trails - so they fade away?
• 'Scroll' the world - a parallax type effect instead of it just being static


Advanced


• Generate the texture with is a 2d image - then use it to construct a '3d' mesh/world (like Tron)
• Modify the implementation to use 'compute' shaders instead of fragment shaders
• Give each 'worm' a larger brain - more data and behaviours
• Create 'volumetric' worms - multiple textures (slices) and the worms can move up/down between the layers



Resources & Links


• Main File Code/Demo LINK

• Noise Fields - Similar concept but using Smooth Noise

• Maggot Version
LINK

• Alternative version LINK








101 WebGPU Programming Projects. WebGPU Development Pixels - coding fragment shaders from post processing to ray tracing! WebGPU by Example: Fractals, Image Effects, Ray-Tracing, Procedural Geometry, 2D/3D, Particles, Simulations WebGPU Games WGSL 2d 3d interactive web-based fun learning WebGPU Compute WebGPU API - Owners WebGPU Development Cookbook - coding recipes for all your webgpu needs! WebGPU & WGSL Essentials: A Hands-On Approach to Interactive Graphics, Games, 2D Interfaces, 3D Meshes, Animation, Security and Production Kenwright graphics and animations using the webgpu api 12 week course kenwright learn webgpu api kenwright programming compute and graphics applications with html5 and webgpu api kenwright real-time 3d graphics with webgpu kenwright webgpu for dummies kenwright webgpu wgsl compute graphics all in one kenwright webgpu api develompent a quick start guide kenwright webgpu by example 2022 kenwright webgpu gems kenwright webgpu interactive compute and graphics visualization cookbook kenwright wgsl webgpu shading language cookbook kenwright WebGPU Shader Language Development: Vertex, Fragment, Compute Shaders for Programmers Kenwright WGSL Fundamentals book kenwright WebGPU Data Visualization Cookbook kenwright Special Effects Programming with WebGPU kenwright WebGPU Programming Guide: Interactive Graphics and Compute Programming with WebGPU & WGSL kenwright Ray-Tracing with WebGPU kenwright



 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.