Mycelial / Trailhouse Source: https://github.com/nicoptere/physarum/tree/da6536f091eef7ad9ecf2180ee0de30a4c06108a Author: Nicolas Barradeau LICENSE file: The Unlicense. package.json lists ISC; both notices are preserved below. Upstream package metadata: { "name": "physarum", "version": "0.0.0", "description": "slime mold simulation", "main": "index.js", "dependencies": {}, "devDependencies": { "file-loader": "^3.0.1", "raw-loader": "^1.0.0", "webpack": "^4.29.0", "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.8", "three": "0.102.1", "dat.gui": "0.7.5" }, "scripts": { "dev": "webpack && webpack-dev-server", "deploy": "webpack -p --config webpack.config.js" }, "keywords": [ "physarum", "simulation", "gpgpu", "slime mold" ], "author": "nicolas barradeau", "license": "ISC" } Adaptation: agent three-sensor trail following and diffusion/decay ported to native WebGL2. Original interface, palettes, keyboard/pointer planting, pause/step/reset and read-only diagnostics. Fixed degrees-to-radians conversion; reduced agents to 16384 and bounded trail field to 640x480. No Three.js/dat.gui/build/runtime dependencies. Upstream update shader: uniform sampler2D input_texture; uniform sampler2D data; uniform vec2 resolution; uniform float time; uniform float sa; uniform float ra; uniform float so; uniform float ss; const float PI = 3.14159265358979323846264;// PI const float PI2 = PI * 2.; const float RAD = 1./PI; const float PHI = 1.61803398874989484820459 * .1;// Golden Ratio const float SQ2 = 1.41421356237309504880169 * 1000.;// Square Root of Two float rand(in vec2 coordinate){ return fract(tan(distance(coordinate*(time+PHI),vec2(PHI,PI*.1)))*SQ2); } float getDataValue(vec2 uv){ return texture2D(data,fract( uv ) ).r; } float getTrailValue(vec2 uv){ return texture2D(data,fract(uv)).g; } varying vec2 vUv; void main(){ //converts degree to radians (should be done on the CPU) float SA = sa * RAD; float RA = ra * RAD; //downscales the parameters (should be done on the CPU) vec2 res = 1. / resolution;//data trail scale vec2 SO = so * res; vec2 SS = ss * res; //uv = input_texture.xy //where to sample in the data trail texture to get the agent's world position vec4 src = texture2D(input_texture,vUv); vec4 val = src; //agent's heading float angle = val.z * PI2; // compute the sensors positions vec2 uvFL=val.xy+vec2(cos(angle-SA),sin(angle-SA)) * SO; vec2 uvF =val.xy+vec2(cos(angle),sin(angle)) * SO; vec2 uvFR=val.xy+vec2(cos(angle+SA),sin(angle+SA))*SO; //get the values unders the sensors float FL = getTrailValue(uvFL); float F = getTrailValue(uvF); float FR = getTrailValue(uvFR); // original implement not very parallel friendly // TODO remove the conditions if( F > FL && F > FR ){ }else if( F .5 ){ angle +=RA; }else{ angle -=RA; } }else if( FLFR){ angle-=RA; } vec2 offset = vec2(cos(angle),sin(angle)) * SS; val.xy += offset; //condition from the paper : move only if the destination is free // if( getDataValue(val.xy) == 1. ){ // val.xy = src.xy; // angle = rand(val.xy+time) * PI2; // } //warps the coordinates so they remains in the [0-1] interval val.xy = fract( val.xy ); //converts the angle back to [0-1] val.z = ( angle / PI2 ); gl_FragColor = val; } Upstream diffusion shader: uniform sampler2D points; uniform sampler2D input_texture; uniform vec2 resolution; uniform float time; uniform float decay; varying vec2 vUv; void main(){ vec2 res = 1. / resolution; float pos = texture2D(points, vUv).r; //accumulator float col = 0.; //blur box size const float dim = 1.; //weight float weight = 1. / pow( 2. * dim + 1., 2. ); for( float i = -dim; i <= dim; i++ ){ for( float j = -dim; j <= dim; j++ ){ vec3 val = texture2D( input_texture, fract( vUv+res*vec2(i,j) ) ).rgb; col += val.r * weight + val.g * weight * .5; } } vec4 fin = vec4( pos * decay, col * decay, .5, 1. ); gl_FragColor = clamp( fin, 0.01, 1. ); }