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 ...

 



Ocean water effect.
Ocean water effect.


Generating Deep Water Ocean Effect (On Shader)


Few Juicy Points
• Generated on the shader (no external textures or files)
• An object is added to the water to emphasis various effect (reflection and refraction)
• Object bounces around due to the water
• Pattern on the object is generated using the object shape/position surface
Interactive so you can move your mouse around to see the shape/water from different angles


Beauty of Water


The ocean is a breathtaking spectacle of beauty and tranquility, a boundless expanse that captivates with its vastness and timeless allure. Its surface shimmers under the sun, shifting in shades from deep sapphire to light turquoise, each wave catching the light in a dance as ancient as the earth itself. The ocean's movement is mesmerizing—gentle ripples glide across calm bays, while powerful waves roll with a grace that's both majestic and soothing. There is a profound simplicity in the way the ocean breathes in rhythmic tides, rising and falling with a calming constancy, connecting shorelines and washing over sands in an eternal cycle. Beneath the surface lies a world of delicate beauty and vibrant life, hidden mysteries that evoke both wonder and peace. The ocean's endless horizons remind us of nature's vastness, inspiring awe and reflection in its quiet, powerful presence.


The output for the example water simulation.
The output for the example water simulation.


Fragment Shader


The complete implementation is in the fragment shader! Essentially a brute force ray-tracer - for each pixel it calculates the intersection with the water surface and then the lighting calculation (reflections, etc).

Complete fragment shader code is given below:

// Don't use images but is an extra for texturing the shape
@group(0) @binding(0) var  mySamplersampler;
@
group(0) @binding(1) var  myTexturetexture_2d<f32>;
@
group(0) @binding(2) var <uniformmytimer f32;
@
group(0) @binding(3) var <uniformmymouse vec2<f32>;

const 
resolution vec2<f32>(512512);

fn 
ToGammacol:vec3<f32> ) -> vec3<f32>
{
    
// Gamma correction
    
let GammaFactor:f32 2.2;
    
// convert back into color values, so the correct light will come out of the monitor
    
return powcolvec3(1.0/GammaFactor) );
}


Calculates camera position and direction based on rotation, zoom, and screen coordinates.
Outputs a camera structure with `pos`, `ray`, and `localRay` for viewing direction and origin.

struct stCam {
    
posvec3<f32>,
    
rayvec3<f32>,
    
localRayvec3<f32>
};

fn 
CamPolarorigin:vec3<f32>, rotation:vec2<f32>, distance:f32zoom:f32fragCoord:vec2<f32> ) -> stCam
{
    var 
camstCam;
    
// get rotation coefficients
    
var cvec2<f32> = vec2(cos(rotation.x),cos(rotation.y));
    var 
tmp vec2(sin(rotation.x),sin(rotation.y));
    
    var 
svec4<f32> = vec4<f32>( tmp.xy, -tmp.xy );

    
// ray in view space
    
cam.ray vec3fragCoord.xy resolution.xy*.5resolution.y*zoom );

    
cam.ray normalize(cam.ray);

    
cam.localRay cam.ray;
    
    
// rotate ray
    
var tmp2 cam.ray.yz*c.xx cam.ray.zy*s.zx;
    
cam.ray vec3cam.ray.xtmp2 );
    
    
tmp2 cam.ray.xz*c.yy cam.ray.zx*s.yw;
    
cam.ray vec3tmp2.xcam.ray.ytmp2.);
   
    
// position camera
    
cam.pos origin distance*vec3(c.x*s.y,s.z,c.x*c.y);
    
    return 
cam;
}


Generates a pseudo-random number based on input `uv` coordinates.
Useful for creating randomness in textures and patterns.

fn random(uvvec2<f32>) -> f32 {
    return 
fract(sin(dot(uvvec2<f32>(12.989878.233))) * 43758.5453);
}


Smooths random values to create a cohesive noise pattern.
Interpolates values between tile corners, resulting in smooth, blended noise.


You can see what the smooth noise function generates.
You can see what the smooth noise function generates.

.

You can try out an interactive noise demo showing the smooth noise scrolling LINK.

fn randomsmoothst:vec2<f32> ) -> f32 
{
    var 
floorst 3.0 ); // uv - 0,   1,   2,   3, 
    
var fractst 3.0 ); // uv - 0-1, 0-1, 0-1, 0-1

    // Four corners in 2D of a tile
    
var random(i);
    var 
random(vec2<f32>(1.00.0));
    var 
random(vec2<f32>(0.01.0));
    var 
random(vec2<f32>(1.01.0));

    
// Ease-in followed by an ease-out (tweening) for f
    // f = 0.5 * (1.0 - cos( 3.14 * f ) ); 
    // version without cos/sin
    // f = 3*f*f - 2*f*f*f;
    
    
3*f*2*f*f*f;
    
    
// bilinear interpolation to combine the values sampled from the 
    // four corners (a,b,c,d), resulting in a smoothly interpolated value.
   
    // Interpolate Along One Axis - interpolate between `a` and `b` using the fractional coordinate `f.x`, 
    // then interpolate between `c` and `d` using the same `f.x`. 
    // This gives two intermediate values, say `e` and `f`.

    // Interpolate Along the Other Axis - linearly interpolate between `e` and `f` 
    // using the fractional coordinate `f.y`. 
    // Final interpolation gives a moothly interpolated value across the square
    
    
var x1 mixabf.);
    var 
x2 mixcdf.);
    
    var 
y1 mixx1x2f.);
    
    return 
y1;
}


Produces 3D noise using smoothed random functions for multi-dimensional use.
The precise version is to refine the noise by scaling input coordinates, creating a sharper noise effect.


fn Noise(xvec3<f32>) -> vec2<f32>
{
    return 
vec2<f32>(
        
randomsmoothx.xy*0.2 ),
        
randomsmoothx.yz*0.129020323 )
        );
}
    
    
fn 
NoisePrecise(xvec3<f32>) -> vec2<f32> {
    return 
Noisex*1.1 );
}


Simulates a dynamic wave effect by repeatedly displacing positions over octaves.
Uses multiple layers of random noise for complex wave shapes.

fn Wavesposin:vec3<f32> ) -> f32
{
    var 
pos posin 0.2*vec3(1,1,1);
    
    const 
octaves:i32 5;
    var 
f:f32 0.0;

    
pos += mytimer*vec3(0,.1,.1);
    for ( var 
i:i32=0octavesi++ )
    {
        
pos = (pos.yzx pos.zyx*vec3(1,-1,1))/sqrt(2.0);
        
f  f*2.0+abs(Noise(pos).x-.5)*2.0;
        
pos *= 2.0;
    }
    
/= exp2(f32(octaves));
    
    return (
.5-f)*1.0;
}


Generates detailed wave shapes with more octaves and smooth blending.
Useful for creating complex water surface dynamics.

fn WavesDetailposin:vec3<f32> ) -> f32
{
    var 
pos posin 0.2*vec3(1,1,1);
    
    const 
octaves:i32 8;
    var 
f:f32 0.0;

    
// need to do the octaves from large to small, otherwise things don't line up
    // (because I rotate by 45 degrees on each octave)
    
pos += mytimer*vec3(0,.1,.1);
    for ( var 
i:i32=0octavesi++ )
    {
        
pos = (pos.yzx pos.zyx*vec3(1,-1,1))/sqrt(2.0);
        
f  f*2.0+abs(NoisePrecise(pos).x-0.5)*2.0;
        
pos *= 2.0;
    }
    
/= exp2(f32(octaves));
    
    return (
.5-f)*1.0;
}


Creates smoother wave effects by applying additional noise blending.
Creates a gentler water surface than other wave functions.

fn WavesSmoothposin:vec3<f32> ) -> f32
{
    var 
pos posin 0.2*vec3(1,1,1);
    
    const 
octaves:i32 2;
    var 
f:f32 0.0;

    
pos += mytimer*vec3(0,.1,.1);
    for ( var 
i:i32=0octavesi++ )
    {
        
pos = (pos.yzx pos.zyx*vec3(1,-1,1))/sqrt(2.0);

        
f  f*2.0+sqrt(pow(NoisePrecise(pos).x-.5,2.0)+.01)*2.0;
        
pos *= 2.0;
    }
    
/= exp2(f32(octaves));
    
    return (
.5-f)*1.0;
}


Smoothly interpolates values between two edges, `edge0` and `edge1`.
Applies smoothing to input `x` to reduce sharp transitions.
Also a builtin version `smoothstep` but the custom version was used so we could tinker with the values while experimenting.

fn mysmoothstepedge0:f32edge1:f32x:f32 ) -> f32
{
    
// Clamp the input value between 0 and 1
    
var t:f32 clamp((edge0) / (edge1 edge0), 0.01.0);
    
// Apply the smoothstep formula
    
return * (3.0 2.0 t);
}


Simulates foam on wave crests based on input position.
Enhances wave realism by adding bright foam to high wave areas.

fn WaveCrestsipos:vec3<f32>, fragCoord:vec2<f32> ) -> f32
{
    var 
pos:vec3<f32> = ipos;
    
pos pos 0.2*vec3<f32>(1,1,1);
    
    const 
octaves1:i32 6;
    const 
octaves2:i32 16;
    var 
f:f32 0.0;

    
pos += mytimer*vec3(0,.1,.1);
    var 
pos2 pos;
    for ( var 
i:i32=0octaves1i++ )
    {
        
pos = (pos.yzx pos.zyx*vec3(1,-1,1))/sqrt(2.0);
        
f*1.5+abs(Noise(pos).x-.5)*2.0;
        
pos *= 2.0;
    }
    
pos pos2 exp2(f32(octaves1));
    
pos.= -.05*mytimer;
    for ( var 
i:i32=octaves1octaves2i++ )
    {
        
pos = (pos.yzx pos.zyx*vec3(1,-1,1))/sqrt(2.0);
        
f  f*1.5+pow(abs(Noise(pos).x-.5)*2.0,1.0);
        
pos *= 2.0;
    }
    
/= 1500.0;
    
    
-= Noisevec3<f32>(fragCoord.xyfragCoord.x*1.29323) ).0.01;
    
    return 
pow(mysmoothstep(.4,-.1,f),6.0);
}


Defines background sky color based on ray direction.
Blends colors to create a horizon and zenith transition.

/*
// Constant sky color
fn Sky( ray:vec3<f32> ) -> vec3<f32>
{
    return vec3(.5,.45,.54);
}
*/

fn Sky(rayvec3<f32>) -> vec3<f32> {
    
let horizon_color vec3(.4,.45,.5); // Light blue near the horizon
    
let zenith_color vec3(.4,.45,.5)*0.1// Darker blue for the sky above

    // Calculate a factor based on the Y component of the ray direction
    // Higher y value means looking up, lower means closer to horizon
    
let blend_factor clamp(ray.0.5 0.50.01.0);

    
// Blend between zenith and horizon colors based on blend_factor
    
return mix(horizon_colorzenith_colorblend_factor);
}


Calculates the boat's position, orientation, and movement based on water surface.
Returns a `stBoat` structure with direction vectors and orientation matrix.

struct stBoat
{
    
right    vec3<f32>, 
    
up       vec3<f32>, 
    
forward  vec3<f32>, 
    
position vec3<f32>, 
    
rotation mat3x3<f32>
};

fn 
ComputeBoatTransform() -> stBoat
{
    var 
samples : array<vec3<f32>, 5>;
    
    
samples[0] = vec3(0,00);
    
samples[1] = vec3(0,0.5);
    
samples[2] = vec3(0,0,-.5);
    
samples[3] = vec3.5,0,0);
    
samples[4] = vec3(-.5,0,0);
    
    
samples[0].WavesSmooth(samples[0]);
    
samples[1].WavesSmooth(samples[1]);
    
samples[2].WavesSmooth(samples[2]);
    
samples[3].WavesSmooth(samples[3]);
    
samples[4].WavesSmooth(samples[4]);

    var 
boatstBoat;
    
boat.position = (samples[0]+samples[1]+samples[2]+samples[3]+samples[4])/5.0;
    
    
boat.right   samples[3]-samples[4];
    
boat.forward samples[1]-samples[2];
    
boat.up      normalize(cross(boat.forward,boat.right));
    
boat.right   normalize(cross(boat.up,boat.forward));
    
boat.forward normalize(boat.forward);

    
boat.rotation mat3x3<f32>(
        
boat.right.xboat.up.xboat.forward.x,
        
boat.right.yboat.up.yboat.forward.y,
        
boat.right.zboat.up.zboat.forward.z
    
);
    
    
// Push the object up or down (under water if we want).
    
boat.position += 0.0*boat.up;
    
    return 
boat;
}



Offset the object so it
Offset the object so it's under water - or just on the surface bobbing in and out - by adjust ting the ComputeBoatTransform() position offset.



Signed distance function for a cube and sphere for ray-marching.

fn sdfCubetestPoint:vec3<f32>,  cubePos:vec3<f32>,  cubeDim:vec3<f32> ) ->f32
{
   var 
d:vec3<f32> = abs(cubePos testPoint) - cubeDim;
   return 
min(max(d.xmax(d.yd.z)), 0.0)
           + 
lengthmax(dvec3<f32>(0.0) ) );
}

fn 
sdfSpheretestPoint:vec3<f32>,  spherePos:vec3<f32>,  sphereRadius:f32 )  ->f32
{
    return 
length(spherePos testPoint) - sphereRadius;
}


Ray-marches along a ray to detect intersections with the boat.
Uses a distance function to trace until hitting the boat or reaching max distance.

fn TraceBoatrayPos:vec3<f32>,  rayDir:vec3<f32>) ->f32
{
    
let boat    ComputeBoatTransform();
    var 
boatPos boat.position;
    
let cubeRot boat.rotation;
    
    var 
0.0;
    for(var 
i:i32 0256i++)
    {
        var 
samplePoint:vec3<f32> = rayPos rayDir*t;
        
        
let localRayPos transpose(cubeRot) * (samplePoint boatPos);
        
        
// Use any sdf shape!  - sphere, cube, ..etc
        //var dist = sdfSphere(samplePoint, boatPos, 1.0 );
        
        
var dist sdfCube(localRayPosvec3(0.001), vec3(1.0) );
        
+= dist;
        if ( 
0.01 || 1000 )
        {
            return 
0.0// no hit
        
}
    }
    return 
t;
}


Calculates the surface normal for the boat at the intersection point.
Uses small offsets to determine normal based on neighboring distances.

fn rayNormalrayPos:vec3<f32>,  rayDir:vec3<f32> ) -> vec3<f32>
{
    var 
0.0001;
    var 
TraceBoat(rayPos,rayDir);
    var 
vec3<f32>(rayPos.srayPos.y,     rayPos.z    );
    var 
vec3<f32>(rayPos.x,     rayPos.srayPos.z    );
    var 
vec3<f32>(rayPos.x,     rayPos.y,     rayPos.s);
    var 
normal normalize(vec3<f32>(
                    
TraceBoat(arayDir) - d,
                     
TraceBoat(brayDir) - d,
                     
TraceBoat(crayDir) - d) );
    return 
normal;
}


Adds color and texture to the boat, simulating wood grain or painted patterns.
Uses noise to vary colors and create realistic surface patterns.

fn ShadeBoatPatternsurfacePos:vec3<f32> ) -> vec3<f32>
{
    
let boat ComputeBoatTransform();
    
let cubePos boat.position// vec3
    
let cubeRot boat.rotation// mat3x3
    
    // Transform surface position into local space
    
let localPos transpose(cubeRot) * (surfacePos cubePos);

    
let frequency 11.0;
    
let color0 vec3(0.50.20.1); // Base color
    
let color1 vec3(0.10.20.6); // Stripe color
    
    
let noisepattern 0.2 
                       
NoiselocalPos*12.0 ).0.3 +
                       
NoiselocalPos*3.0 ).0.7;
    
    if ( 
floor(localPos.frequency) % 4.0 == 0.0 ||
         
floor(localPos.frequency) % 4.0 == 0.0 || 
         
floor(localPos.frequency) % 4.0 == 0.0 )
    {
        return 
color0 noisepattern;
    }
    return 
color1 noisepattern;
    
    
}


Colors the boat surface based on light direction, reflections, and textures.
Calculates the boat's shading using normals, lighting, and fresnel reflections.

fn ShadeBoatposin:vec3<f32>, ray:vec3<f32> ) -> vec3<f32>
{
    var 
t:f32 TraceBoatposinray );
    var 
hp posin ray*t;
    var 
norm rayNormalposinray );
      
    var 
lightDir:vec3<f32> = normalize(vec3(-2,3,1));
    var 
ndotl:f32 0.2 absdot(norm,lightDir) );
    
    
// allow some light bleed, as if it's subsurface scattering through plastic
    
var light:vec3<f32> = mysmoothstep(-.1,1.0,ndotl)*vec3(1.0,.9,.8)+vec3(.06,.1,.1);

    var 
albedo:vec3<f32> = ShadeBoatPattern(hp); 

    var 
col:vec3<f32> = albedo*ndotl;
    
    
// specular
    
var h:vec3<f32> = normalize(lightDir-ray);
    var 
s:f32 pow(max(0.0,dot(norm,h)),100.0)*100.0/32.0;
    
    var 
specular:vec3<f32> = s*vec3(1.1);

    var 
rr:vec3<f32> = reflect(ray,norm);
    
specular += mixvec3(0,.04,.04), Sky(rr), mysmoothstep( -.1.1rr.) );
    
    var 
ndotr:f32 dot(norm,ray);
    var 
fresnel:f32 pow(1.0-abs(ndotr),5.0);
    
fresnel mix.0011.0fresnel );

    
col mixcolspecularfresnel );
    
    return 
col;
}


Calculates distance from a point to the ocean surface, modified by wave height.
Used for ray-marching to the water surface. Refined version is for more detail using additional wave calculations.

fn OceanDistanceFieldpos:vec3<f32> ) -> f32
{
    return 
pos.Waves(pos);
}

fn 
OceanDistanceFieldDetailpos:vec3<f32> ) -> f32
{
    return 
pos.WavesDetail(pos);
}


Computes surface normals for the ocean to enhance lighting and reflection.
Uses offset distances to measure wave slope and direction at a point.

fn OceanNormalpos:vec3<f32> ) -> vec3<f32>
{
    var 
norm:vec3<f32>;
    var 
d:vec2<f32> = vec2(.01*length(pos),0);
    
    
norm.OceanDistanceFieldDetailpos+d.xyy )-OceanDistanceFieldDetailpos-d.xyy );
    
norm.OceanDistanceFieldDetailpos+d.yxy )-OceanDistanceFieldDetailpos-d.yxy );
    
norm.OceanDistanceFieldDetailpos+d.yyx )-OceanDistanceFieldDetailpos-d.yyx );

    return 
normalize(norm);
}


Ray-marches through the scene to detect intersections with the ocean surface.
Stops when close to the surface or exceeding max distance.

fn TraceOceanpos:vec3<f32>, ray:vec3<f32> ) -> f32
{
    var 
h:f32 1.0;
    var 
t:f32 0.0;
    for ( var 
i:i32=0100i++ )
    {
        if ( 
.01 || 100.0 )
        {
            break;
        }
        
OceanDistanceFieldpos+t*ray );
        
+= h;
    }
    
    if ( 
.1 )
    {
        return 
0.0;
    }
    return 
t;
}


Colors the ocean surface by calculating reflections, foam, and fresnel effect.
Blends refractions and reflections for realistic water appearance.

fn ShadeOceanpos:vec3<f32>, ray:vec3<f32>, fragCoord:vec2<f32> ) -> vec3<f32>
{
    var 
norm:vec3<f32> = OceanNormal(pos);
    var 
ndotr:f32 dot(ray,norm);

    var 
fresnel:f32 pow(1.0-abs(ndotr),5.0);
    
    var 
reflectedRay:vec3<f32> = ray-2.0*norm*ndotr;
    var 
refractedRay:vec3<f32> = ray+(-cos(1.33*acos(-ndotr))-ndotr)*norm;    
    
refractedRay normalize(refractedRay);

    const 
episonFudge:f32 0.0;
    
    
// reflection
    
var reflection:vec3<f32> = Sky(reflectedRay);
    var 
t:f32 TraceBoatpos-episonFudge*reflectedRayreflectedRay );
    
    if ( 
0.0 )
    {
        
reflection ShadeBoatpos-episonFudge*reflectedRayreflectedRay );
    }

    
// refraction
    
TraceBoatpos-episonFudge*refractedRayrefractedRay );
    
    var 
col:vec3<f32> = vec3(0,.04,.04); // under-sea color
    
if ( 0.0 )
    {
        
col mixcolShadeBoatpos-episonFudge*refractedRayrefractedRay ), exp(-t) );
    }
    
    
col mixcolreflectionfresnel );
    
    
// foam
    
col mixcolvec3(1), WaveCrests(pos,fragCoord) );
    
    return 
col;
}


Primary fragment shader function for rendering the scene.
Determines the camera view and calculates shading for sky, ocean, and boat.

// Shader entry point
@fragment
fn main(@location(0uvs    vec2<f32>) -> @location(0vec4<f32
{
    var 
fragCoord uvs resolution;

    var 
camRot:vec2<f32> = vec2(.5,.5) + vec2(-.35,4.5)*(mymouse.yx/resolution.yx);

    var 
cam CamPolarvec3(0), camRot5.01.0fragCoord );
    
    var 
to:f32 TraceOceancam.poscam.ray );
    var 
tb:f32 TraceBoatcam.poscam.ray );
    
    var 
resultvec3<f32>;
    if ( 
to 0.0 && ( to tb || tb == 0.0 ) )
    {
        
result ShadeOceancam.pos+cam.ray*tocam.rayfragCoord );
    }
    else if ( 
tb 0.0 )
    {
        
result ShadeBoatcam.poscam.ray );
    }
    else
    {
        
result Skycam.ray );
    }
    
 
    
// vignette effect
    
result *= 1.1 mysmoothstep.351.0cam.localRay.);
    
    var 
fragColor vec4(ToGamma(result),1.0);
    
    return 
fragColor;
}


Things to Try


The example as only scratched the surface of what is possible an what you can do next! Some interesting and fun ideas to take it further include:

• Tweak the sea noise generation (it's okay, but could still be improved)
• Different noise patterns can generate different ocean water types (deep, shallow, ...)
• Use 2d map to make different parts of the water use different noise (e.g., close to short or deep water, behaves differently)
• Also modify the noise - so if it's close to the object uses a different noise pattern
• Add some under water creatures, whale or fished?
• Add some 'splash' effects (particles)
• SDF shape - simple box or sphere - but try other shapes
• SDF shape of a 'bottle' - but make it transparent? (glass bottle floating at sea)
• Add background sky/sunset
• Draw lots of things int he water (e.g., rubbish, coke cans, use 'mod' so you can draw hundreds of them)
• Make the water effect into game? little boat driving around?
• Use the texture for the floating object (instead of a generated pattern - texture mapped ot the surface)
• Add interface to allow users to tinker with the variables
• Create a demo scene with sand islands coming out of the water, fish jumping, palm trees, and a sun moving across the sky.



Resources & Links


• WebGPU Ocean/Sea Effect (Full Code) LINK

• Example showing smooth noise on shader LINK

• Vulkan Implementation 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.