Skip to content
blotter

API

shaders

GLSL helpers as strings, ready to interpolate at the top of a mainImage. The bundled materials are built from them.

On this page

Each export is a string of GLSL function declarations. Put it before your mainImage in the shader body and call the functions it defines. Expand a snippet below to read exactly what it declares.

import { Material, shaders } from "blotter.ts";

const material = new Material({
  mainImage: `
    ${shaders.noise2d}

    void mainImage( out vec4 mainImage, in vec2 fragCoord ) {
      vec2 uv = fragCoord / uResolution;
      uv.x += snoise(uv * 3.0 + uGlobalTime) * 0.02;
      mainImage = textTexture(uv);
    }
  `,
});

shaders.blending

Photoshop-style blend modes as functions of two colours: normalBlend, multiplyBlend, screenBlend, overlayBlend and more.

GLSL source
//
// Author : Bradley Griffith
// License : Distributed under the MIT License.
//

// Returns the resulting blend color by blending a top color over a base color
highp vec4 normalBlend(highp vec4 topColor, highp vec4 baseColor) {
  highp vec4 blend = vec4(0.0);
  // HACK
  // Cant divide by 0 (see the 'else' alpha) and after a lot of attempts
  // this simply seems like the only solution Im going to be able to come up with to get alpha back.
  if (baseColor.a == 1.0) {
    baseColor.a = 0.9999999;
  };
  if (topColor.a >= 1.0) {
    blend.a = topColor.a;
    blend.r = topColor.r;
    blend.g = topColor.g;
    blend.b = topColor.b;
  } else if (topColor.a == 0.0) {
    blend.a = baseColor.a;
    blend.r = baseColor.r;
    blend.g = baseColor.g;
    blend.b = baseColor.b;
  } else {
    blend.a = 1.0 - (1.0 - topColor.a) * (1.0 - baseColor.a); // alpha
    blend.r = (topColor.r * topColor.a / blend.a) + (baseColor.r * baseColor.a * (1.0 - topColor.a) / blend.a);
    blend.g = (topColor.g * topColor.a / blend.a) + (baseColor.g * baseColor.a * (1.0 - topColor.a) / blend.a);
    blend.b = (topColor.b * topColor.a / blend.a) + (baseColor.b * baseColor.a * (1.0 - topColor.a) / blend.a);
  }
  return blend;
}
// Returns a vec4 representing the original top color that would have been needed to blend
//  against a passed in base color in order to result in the passed in blend color.
highp vec4 normalUnblend(highp vec4 blendColor, highp vec4 baseColor) {
  highp vec4 unblend = vec4(0.0);
  // HACKY
  // Cant divide by 0 (see alpha) and after a lot of attempts
  // this simply seems like the only solution Im going to be able to come up with to get alpha back.
  if (baseColor.a == 1.0) {
    baseColor.a = 0.9999999;
  }
  unblend.a = 1.0 - ((1.0 - blendColor.a) / (1.0 - baseColor.a));
  // Round to two decimal places
  unblend.a = (sign(100.0 * unblend.a) * floor(abs(100.0 * unblend.a) + 0.5)) / 100.0;
  if (unblend.a >= 1.0) {
    unblend.r = blendColor.r;
    unblend.g = blendColor.g;
    unblend.b = blendColor.b;
  } else if (unblend.a == 0.0) {
    unblend.r = baseColor.r;
    unblend.g = baseColor.g;
    unblend.b = baseColor.b;
  } else {
    unblend.r = (blendColor.r - (baseColor.r * baseColor.a * (1.0 - unblend.a) / blendColor.a)) / (unblend.a / blendColor.a);
    unblend.g = (blendColor.g - (baseColor.g * baseColor.a * (1.0 - unblend.a) / blendColor.a)) / (unblend.a / blendColor.a);
    unblend.b = (blendColor.b - (baseColor.b * baseColor.a * (1.0 - unblend.a) / blendColor.a)) / (unblend.a / blendColor.a);
  }
  return unblend;
}

shaders.blinnPhongSpecular

A Blinn-Phong specular term for faking a highlight on the text.

GLSL source
//
// Author : Reza Ali
// License : Distributed under the MIT License.
//

float blinnPhongSpecular( vec3 lightDirection, vec3 viewDirection, vec3 surfaceNormal, float shininess ) {

  //Calculate Blinn-Phong power
  vec3 H = normalize(viewDirection + lightDirection);
  return pow(max(0.0, dot(surfaceNormal, H)), shininess);
}

shaders.easing

The Penner easing curves (quadraticIn, cubicInOut, elasticOut…) for shaping time.

GLSL source
//
// Author : Reza Ali
// License : Distributed under the MIT License.
//

float linear( float t, float b, float c, float d )
{
    return t * ( c / d ) + b;
}

float linear( float t )
{
    return t;
}

float inQuad( float t, float b, float c, float d )
{
    return c * ( t /= d ) * t + b;
}

float inQuad( float t )
{
    return t * t;
}

float outQuad( float t, float b, float c, float d )
{
    return -c * ( t /= d ) * ( t - 2.0 ) + b;
}

float outQuad( float t )
{
    return - ( t -= 1.0 ) * t + 1.0;
}

float inOutQuad( float t, float b, float c, float d )
{
    if( ( t /= d / 2.0 ) < 1.0 ) {
      return c / 2.0 * t * t + b;
    }
    return - c / 2.0 * ( ( --t ) * ( t - 2.0 ) - 1.0 ) + b;
}

float inOutQuad( float t )
{
    if( ( t /= 0.5 ) < 1.0 ) return 0.5 * t * t;
    return -0.5 * ( ( --t ) * ( t-2 ) - 1 );
}

float inCubic( float t, float b, float c, float d )
{
    return c * ( t /= d ) * t * t + b;
}

float inCubic( float t )
{
    return t * t * t;
}

float outCubic( float t, float b, float c, float d )
{
    return c * ( ( t = t/d - 1.0 ) * t * t + 1.0 ) + b;
}

float outCubic( float t )
{
    return ( ( --t ) * t * t + 1.0 );
}

float inOutCubic( float t, float b, float c, float d )
{
    if( ( t /= d / 2.0 ) < 1.0 ) return  c / 2.0 * t * t * t + b;
    return c / 2.0 * ( ( t -= 2.0 ) * t * t + 2.0 ) + b;
}

float inOutCubic( float t )
{
    if( ( t /= 0.5 ) < 1.0 ) return 0.5 * t * t * t;
    return 0.5 * ( ( t -= 2.0 ) * t * t + 2.0 );
}

float inQuart( float t, float b, float c, float d )
{
    return c * ( t /= d ) * t * t * t + b;
}

float inQuart( float t )
{
    return t * t * t * t;
}

float outQuart( float t, float b, float c, float d )
{
    return -c * ( ( t = t/d - 1.0 ) * t * t * t - 1.0 ) + b;
}

float outQuart( float t )
{
    return - ( ( --t ) * t * t * t - 1.0 );
}

float inOutQuart( float t, float b, float c, float d )
{
    if ( ( t /= d / 2.0 ) < 1.0 ) return c / 2.0 * t * t * t * t + b;
    return -c / 2.0 * ( ( t -= 2.0 ) * t * t * t - 2.0 ) + b;
}

float inOutQuart( float t )
{
    if ( (t /= 0.5 ) < 1.0 ) return 0.5 * t * t * t * t;
    return -0.5 * ( ( t -= 2.0 ) * t * t * t - 2.0 );
}

float inQuint( float t, float b, float c, float d )
{
    return c * ( t /= d ) * t * t * t * t + b;
}

float inQuint( float t )
{
    return t * t * t * t * t;
}

float outQuint( float t, float b, float c, float d )
{
    return c * ( ( t = t/d - 1.0) * t * t * t * t + 1.0 ) + b;
}

float outQuint( float t )
{
    return ( ( --t ) * t * t * t * t + 1.0 );
}

float inOutQuint( float t, float b, float c, float d )
{
    if( ( t /= d /2.0 ) < 1.0 ) return  c / 2.0 * t * t * t * t * t + b;
    return c / 2.0 * ( ( t -= 2.0 ) * t * t * t * t + 2.0) + b;
}

float inOutQuint( float t )
{
    if ( ( t /= 0.5 ) < 1.0 ) return 0.5 * t * t * t * t * t;
    return 0.5 * ( ( t -= 2 ) * t * t * t * t + 2.0 );
}

float inSine( float t, float b, float c, float d )
{
    return -c * cos( t / d * ( 1.5707963268 ) ) + c + b;
}

float inSine( float t )
{
    return -1.0 * cos( t * ( 1.5707963268 ) ) + 1.0;
}

float outSine( float t, float b, float c, float d )
{
    return c * sin( t / d * ( 1.5707963268 ) ) + b;
}

float outSine( float t )
{
    return sin( t * ( 1.5707963268 ) );
}

float inOutSine( float t, float b, float c, float d )
{
    return - c / 2.0 * ( cos( 3.1415926536 * t / d ) - 1.0 ) + b;
}

float inOutSine( float t )
{
    return -0.5 * ( cos( 3.1415926536 * t ) - 1.0 );
}

float inExpo( float t, float b, float c, float d )
{
    return ( t == 0.0 ) ? b : c * pow( 2.0, 10.0 * ( t / d - 1.0 ) ) + b;
}

float inExpo( float t )
{
    return ( t == 0 ) ? 0.0 : pow( 2.0, 10.0 * ( t - 1.0 ) );
}

float outExpo( float t, float b, float c, float d )
{
    return ( t == d ) ? b + c : c * ( - pow( 2.0, -10.0 * t / d ) + 1.0 ) + b;
}

float outExpo( float t )
{
    return ( t == 1.0 ) ? 1.0 : ( - pow( 2.0, -10.0 * t ) + 1.0 );
}

float inOutExpo( float t, float b, float c, float d )
{
    if( t == 0 ) return b;
    if( t == d ) return b + c;
    if(( t /= d / 2.0 ) < 1.0 ) return c / 2.0 * pow( 2.0, 10.0 * ( t - 1.0 ) ) + b;
    return c / 2.0 * ( - pow( 2.0, -10.0 * --t ) + 2.0 ) + b;
}

float inOutExpo( float t )
{
    if( t == 0.0 ) return 0.0;
    if( t == 1.0 ) return 1.0;
    if( ( t /= 0.5 ) < 1.0 ) return 0.5 * pow( 2.0, 10.0 * ( t - 1.0 ) );
    return 0.5 * ( - pow( 2.0, -10.0 * --t ) + 2.0 );
}

float inCirc( float t, float b, float c, float d )
{
    return -c * ( sqrt( 1.0 - (t/=d)*t) - 1) + b;
}

float inCirc( float t )
{
    return - ( sqrt( 1.0 - t*t) - 1);
}

float outCirc( float t, float b, float c, float d )
{
    return c * sqrt( 1.0 - (t=t/d-1)*t) + b;
}

float outCirc( float t )
{
    return sqrt( 1.0 - (--t)*t);
}

float inOutCirc( float t, float b, float c, float d )
{
    if ( ( t /= d / 2.0 ) < 1 ) return - c / 2.0 * ( sqrt( 1.0 - t * t ) - 1.0 ) + b;
    return c / 2.0 * ( sqrt( 1.0 - ( t -= 2.0 ) * t ) + 1.0 ) + b;
}

float inOutCirc( float t )
{
    if( ( t /= 0.5 ) < 1.0 ) return -0.5 * ( sqrt( 1.0 - t * t ) - 1.0 );
    return 0.5 * ( sqrt( 1.0 - ( t -= 2.0 ) * t ) + 1.0 );
}

float inElastic( float t, float b, float c, float d )
{
    float s = 1.70158; float p = 0.0; float a = c;
    if( t == 0 ) return b;  if( ( t /= d ) == 1 ) return b + c;
    p = d * 0.3;
    if( a < abs( c ) ) { a = c; float s = p / 4.0; }
    else s = 0.1591549431 * p / ( 6.2831853072 ) * asin( c / a );
    return -( a * pow( 2.0, 10.0 * ( t -= 1.0 ) ) * sin( ( t * d - s ) * ( 6.2831853072 ) / p ) )  + b;
}

float inElastic( float t )
{
    float s = 1.70158; float p = 0.0; float a = 1.0;
    if( t == 0.0 ) return 0.0;
    if( t == 1.0 ) return 1.0;
    p = 0.3;
    s = p / ( 6.2831853072 ) * asin( 1.0 / a );
    return -( a * pow( 2.0, 10.0 * ( t -= 1.0 ) ) * sin( ( t - s ) * ( 6.2831853072 ) / p ) );
}

float outElastic( float t, float b, float c, float d )
{
    float s = 1.70158; float p = 0.0; float a = c;
    if( t == 0.0 ) return b;
    if( (t /= d ) == 1.0 ) return b + c;
    p = d * 0.3;
    if( a < abs( c ) ) { a = c; s = p / 4.0; }
    else { s = p / ( 6.2831853072 ) * asin( c / a ); }
    return a * pow( 2.0, -10.0 * t ) * sin( ( t * d - s ) * ( 6.2831853072 ) / p ) + c + b;
}

float outElastic( float t )
{
    float s = 1.70158; float p = 0.0 ; float a = 1.0;
    if( t == 0.0 ) return 0.0;  if( t == 1.0 ) return 1.0;
    p = 0.3;
    s = p / ( 6.2831853072 ) * asin( 1.0 / a );
    return a * pow( 2.0, -10.0 * t ) * sin( ( t - s ) * ( 6.2831853072 ) / p ) + 1.0;
}

float inOutElastic( float t, float b, float c, float d )
{
    float s = 1.70158; float p = 0.0; float a = c;
    if( t == 0.0 ) return b;
    if( ( t /= d / 2.0 ) == 2.0 ) return b + c;
    p = d * ( 0.3 * 1.5 );
    if( a < abs( c ) ) { a = c; s = p / 4.0; }
    else { s = p / ( 6.2831853072 ) * asin( c / a ); }
    if( t < 1.0 ) return -0.5 * ( a * pow( 2.0, 10.0 * ( t -= 1.0 ) ) * sin( ( t * d - s ) * ( 6.2831853072 ) / p ) ) + b;
    return a * pow( 2.0, -10.0 * ( t -= 1.0 ) ) * sin( ( t * d - s ) * ( 6.2831853072 ) / p ) * 0.5 + c + b;
}

float inOutElastic( float t )
{
    float s = 1.70158; float p = 0; float a = 1.0;
    if( t == 0 ) return 0.0;
    if( ( t /= 0.5 ) == 2.0 ) return 1.0;
    p = ( 0.3 * 1.5 );
    s = p / ( 6.2831853072 ) * asin( 1.0 / a );
    if( t < 1.0 ) return -0.5 * ( a * pow( 2.0, 10.0 * ( t -= 1.0 ) ) * sin( ( t - s ) * ( 6.2831853072 ) / p ) );
    return a * pow( 2.0, -10.0 * ( t -= 1.0 ) ) * sin( ( t - s ) * ( 6.2831853072 ) / p ) * 0.5 + 1.0;
}

float inBack( float t, float b, float c, float d )
{
    float s = 1.70158;
    return c * ( t /= d ) * t * ( ( s + 1.0 ) * t - s ) + b;
}

float inBack( float t )
{
    float s = 1.70158;
    return t * t * ( ( s + 1.0 ) * t - s);
}

float outBack( float t,  float b,  float c,  float d )
{
    float s = 1.70158;
    return c * ( ( t = t / d - 1.0 ) * t * ( ( s + 1.0 ) * t + s ) + 1.0 ) + b;
}

float outBack( float t )
{
    float s = 1.70158;
    return ( ( --t ) * t * ( ( s + 1.0 ) * t + s ) + 1.0);
}

float inOutBack( float t, float b, float c, float d )
{
    float s = 1.70158;
    if( ( t /= d / 2.0 ) < 1.0 ) return c / 2.0 * ( t * t * ( ( ( s *= 1.525 ) + 1.0 ) * t - s ) ) + b;
    return c / 2.0 * ( ( t -= 2.0 ) * t * ( ( ( s *= ( 1.525 ) ) + 1.0 ) * t + s ) + 2.0 ) + b;
}

float inOutBack( float t )
{
    float s = 1.70158;
    if( ( t /= 0.5 ) < 1.0 ) return 0.5 * ( t * t * ( ( ( s *= 1.525 ) + 1.0 ) * t - s ) );
    return 0.5 * ( ( t -= 2 ) * t * ( ( ( s *= ( 1.525 ) ) + 1.0 ) * t + s ) + 2.0 );
}

float outBounce( float t, float b, float c, float d )
{
    if( ( t /= d ) < ( 1.0 / 2.75 ) ) {
        return c * ( 7.5625 * t * t ) + b;
    } else if ( t < ( 2.0 / 2.75 ) ) {
        return c * ( 7.5625 * ( t -= ( 1.5 / 2.75 ) ) * t + 0.75 ) + b;
    } else if ( t < ( 2.5 / 2.75 ) ) {
        return c * ( 7.5625 * ( t -= ( 2.25 / 2.75 ) ) * t + 0.9375 ) + b;
    } else {
        return c * ( 7.5625 * ( t -= ( 2.625 / 2.75 ) ) * t + 0.984375 ) + b;
    }
}

float outBounce( float t )
{
    if( t < ( 1.0 / 2.75 ) ) {
        return ( 7.5625 * t * t );
    } else if ( t < ( 2.0 / 2.75 ) ) {
        return ( 7.5625 * ( t-= ( 1.5 / 2.75 ) ) * t + .75 );
    } else if ( t < ( 2.5 / 2.75 ) ) {
        return ( 7.5625 * ( t -= ( 2.25 / 2.75 ) ) * t + 0.9375 );
    } else {
        return ( 7.5625 * ( t -= ( 2.625 / 2.75 ) ) * t + 0.984375 );
    }
}

float inBounce( float t, float b, float c, float d )
{
    return c - outBounce( d - t, 0.0, c, d ) + b;
}

float inBounce( float t )
{
    return 1.0 - outBounce( 1.0 - t);
}

float inOutBounce( float t, float b, float c, float d )
{
    if ( t < d /2.0 ) return inBounce ( t * 2.0, 0.0, c, d ) * 0.5 + b;
    return outBounce ( t * 2.0 - d, 0, c, d ) * 0.5 + c * 0.5 + b;
}

float inOutBounce( float t )
{
    if ( t < 0.5 ) return inBounce( t * 2.0 ) * 0.5;
    return outBounce( t * 2.0 - 1.0 ) * 0.5 + 0.5;
}

shaders.gamma

sRGB to linear and back: toLinear and toGamma.

GLSL source
//
// Author : Reza Ali
// License : Distributed under the MIT License.
//

const vec3 cGammaCorrection = vec3( 0.4545454545 );

vec3 gamma( in vec3 color )
{
  return pow( color, cGammaCorrection );
}

vec4 gamma( in vec4 color )
{
  return vec4( gamma( color.rgb ), color.a );
}

shaders.inf

A large constant standing in for infinity.

GLSL source
//
// Author : Bradley Griffith
// License : Distributed under the MIT License.
//
bool blotterIsInf(float val) {
    return (val != 0.0 && val * 2.0 == val) ? true : false;
}

shaders.lineMath

Distance from a point to a line or segment, projections, and intersections.

GLSL source
//
// Author : Bradley Griffith
// License : Distributed under the MIT License.
//
bool blotterIsInf(float val) {
    return (val != 0.0 && val * 2.0 == val) ? true : false;
}

//
// Author : Bradley Griffith
// License : Distributed under the MIT License.
//

// Returns the slope of a line given the degrees of the angle on which that line is rotated;
float slopeForDegrees(float deg) {
    // Ensure degrees stay withing 0.0 - 360.0
    deg = mod(deg, 360.0);
    float radians = deg * (PI / 180.0);
    return tan(radians);
}
// Given x, a slope, and another point, find y for x.
float yForXOnSlope(float x, float slope, vec2 p2) {
    return -1.0 * ((slope * (p2.x - x)) - p2.y);
}
// Given y, a slope, and another point, find x for y.
float xForYOnSlope(float y, float slope, vec2 p2) {
    return ((y - p2.y) + (slope * p2.x)) / slope;
}
// Returns slope adjusted for screen ratio.
float normalizedSlope(float slope, vec2 resolution) {
    vec2 p = vec2(1.0) / resolution;
    return ((slope * 100.0) / p.x) / (100.0 / p.x);
}
// Returns offsets (+/-) for any coordinate at distance given slope.
//   Note: This function does not normalize distance.
//   Note: This function does not adjust slope for screen ratio.
vec2 offsetsForCoordAtDistanceOnSlope(float d, float slope) {
    return vec2(
        (d * cos(atan(slope))),
        (d * sin(atan(slope)))
    );
}
// Returns a boolean designating whether or not an infinite line intersects with an infinite line, and sets an `out` variable for the intersection point if it is found.
//   Note: This function does not adjust slope for screen ratio.
bool lineLineIntersection (out vec2 intersect, in vec2 p1, in float m1, in vec2 p2, in float m2) {
    // See: http://gamedev.stackexchange.com/questions/44720/line-intersection-from-parametric-equation
    //      http://stackoverflow.com/questions/41687083/formula-to-determine-if-an-infinite-line-and-a-line-segment-intersect/41687904#41687904
    bool isIntersecting = false;
    float dx = 1.0;
    float dy = m1;
    float dxx = 1.0;
    float dyy = m2;
    float denominator = ((dxx * dy) - (dyy * dx));
    if (denominator == 0.0) {
        // Lines are parallel
        return isIntersecting;
    }
    if (blotterIsInf(dy)) {
        float y = yForXOnSlope(p1.x, m2, p2);
        isIntersecting = true;
        intersect = vec2(p1.x, y);
        return isIntersecting;
    }
    if (blotterIsInf(dyy)) {
        float y = yForXOnSlope(p2.x, m1, p1);
        isIntersecting = true;
        intersect = vec2(p2.x, y);
        return isIntersecting;
    }
    float u = ((dx * (p2.y - p1.y)) + (dy * (p1.x - p2.x))) / denominator;
    isIntersecting = true;
    intersect = p2 + (u * vec2(dxx, dyy));
    return isIntersecting;
}
// Returns a boolean designating whether or not an infinite line intersects with a line segment, and sets an `out` variable for the intersection point if it is found.
//   Note: This function does not adjust slope for screen ratio.
bool lineLineSegmentIntersection (out vec2 intersect, in vec2 point, in float m, in vec2 pA, in vec2 pB) {
    // See: http://gamedev.stackexchange.com/questions/44720/line-intersection-from-parametric-equation
    //      http://stackoverflow.com/questions/41687083/formula-to-determine-if-an-infinite-line-and-a-line-segment-intersect/41687904#41687904
    bool isIntersecting = false;
    float dx = 1.0;
    float dy = m;
    float dxx = pB.x - pA.x;
    float dyy = pB.y - pA.y;
    float denominator = ((dxx * dy) - (dyy * dx));
    if (denominator == 0.0 || (blotterIsInf(dyy / dxx) && blotterIsInf(dy))) {
        // Lines are parallel
        return isIntersecting;
    }
    if (blotterIsInf(dy)) {
        float m2 = dyy / dxx;
        float y = yForXOnSlope(point.x, m2, pB);
        isIntersecting = true;
        intersect = vec2(point.x, y);
        return isIntersecting;
    }
    float u = ((dx * (pA.y - point.y)) + (dy * (point.x - pA.x))) / denominator;
    if (u >= 0.0 && u <= 1.0) {
        // Intersection occured on line segment
        isIntersecting = true;
        intersect = pA + (u * vec2(dxx, dyy));
    }
    return isIntersecting;
}
// Dev Note: Terrible code. Needs refactor. Just trying to find 
//   which two edges of the rect the intersections occur at.
void intersectsOnRectForLine(out vec2 iA, out vec2 iB, in vec2 rMinXY, in vec2 rMaxXY, in vec2 point, in float slope) {
    bool firstIntersectFound = false;
    vec2 intersectLeft = vec2(0.0);
    vec2 intersectTop = vec2(0.0);
    vec2 intersectRight = vec2(0.0);
    vec2 intersectBottom = vec2(0.0);
    bool intersectsLeft = lineLineSegmentIntersection(intersectLeft, point, slope, rMinXY, vec2(rMinXY.x, rMaxXY.y));
    bool intersectsTop = lineLineSegmentIntersection(intersectTop, point, slope, vec2(rMinXY.x, rMaxXY.y), rMaxXY);
    bool intersectsRight = lineLineSegmentIntersection(intersectRight, point, slope, rMaxXY, vec2(rMaxXY.x, rMinXY.y));
    bool intersectsBottom = lineLineSegmentIntersection(intersectBottom, point, slope, rMinXY, vec2(rMaxXY.x, rMinXY.y));
    if (intersectsLeft) {
        if (firstIntersectFound) {
            iB = intersectLeft;
        }
        else {
            iA = intersectLeft;
            firstIntersectFound = true;
        }
    }
    if (intersectsTop) {
        if (firstIntersectFound) {
            iB = intersectTop;
        }
        else {
            iA = intersectTop;
            firstIntersectFound = true;
        }
    }
    if (intersectsRight) {
        if (firstIntersectFound) {
            iB = intersectRight;
        }
        else {
            iA = intersectRight;
            firstIntersectFound = true;
        }
    }
    if (intersectsBottom) {
        if (firstIntersectFound) {
            iB = intersectBottom;
        }
        else {
            iA = intersectBottom;
        }
    }
}

shaders.map

Remap a value from one range to another.

GLSL source
//
// Author : Reza Ali
// License : Distributed under the MIT License.
//

float map( float value, float inMin, float inMax, float outMin, float outMax )
{
    return ( (value - inMin) / ( inMax - inMin ) * ( outMax - outMin ) ) + outMin;
}

shaders.noise

One-dimensional value noise.

GLSL source
//
// Author : Patricio Gonzalez Vivo and Jen Lowe
// License : Distributed under the MIT License.
// Source : https://github.com/patriciogonzalezvivo/thebookofshaders
//
float random (in float _x) {
    return fract(sin(_x)*1e4);
}

float random (in vec2 co) {
    return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);
}

float noise (in float _x) {
    float i = floor(_x);
    float f = fract(_x);
    float u = f * f * (3.0 - 2.0 * f);
    return mix(random(i), random(i + 1.0), u);
}

float noise (in vec2 _st) {
    vec2 i = floor(_st);
    vec2 f = fract(_st);
    // Four corners in 2D of a tile
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));
    vec2 u = f * f * (3.0 - 2.0 * f);
    return mix(a, b, u.x) + 
            (c - a)* u.y * (1.0 - u.x) + 
            (d - b) * u.x * u.y;
}

shaders.noise2d

Two-dimensional simplex noise: snoise(vec2).

GLSL source
//
// Description : Array and textureless GLSL 2D simplex noise function.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//

vec2 n2mod289(vec2 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 n2mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 n2mod289(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x) {
  return n2mod289(((x*34.0)+1.0)*x);
}

float snoise(vec2 v)
  {
  const vec4 C = vec4(0.211324865405187,  // (3.0-sqrt(3.0))/6.0
                      0.366025403784439,  // 0.5*(sqrt(3.0)-1.0)
                     -0.577350269189626,  // -1.0 + 2.0 * C.x
                      0.024390243902439); // 1.0 / 41.0
// First corner
  vec2 i  = floor(v + dot(v, C.yy) );
  vec2 x0 = v -   i + dot(i, C.xx);

// Other corners
  vec2 i1;
  //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
  //i1.y = 1.0 - i1.x;
  i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  // x0 = x0 - 0.0 + 0.0 * C.xx ;
  // x1 = x0 - i1 + 1.0 * C.xx ;
  // x2 = x0 - 1.0 + 2.0 * C.xx ;
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;

// Permutations
  i = n2mod289(i); // Avoid truncation effects in permutation
  vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
   + i.x + vec3(0.0, i1.x, 1.0 ));

  vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m ;
  m = m*m ;

// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;

// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
  m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );

// Compute final noise value at P
  vec3 g;
  g.x  = a0.x  * x0.x  + h.x  * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

shaders.noise3d

Three-dimensional simplex noise: snoise(vec3). The usual choice for animating a 2D field over time.

GLSL source
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
//               noise functions.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//

vec2 n3mod289(vec2 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 n3mod289(vec3 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 n3mod289(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec4 permute(vec4 x) {
     return n3mod289(((x*34.0)+1.0)*x);
}

vec4 taylorInvSqrt(vec4 r)
{
  return 1.79284291400159 - 0.85373472095314 * r;
}

float snoise(vec3 v)
  {
  const vec2  C = vec2(1.0/6.0, 1.0/3.0) ;
  const vec4  D = vec4(0.0, 0.5, 1.0, 2.0);

// First corner
  vec3 i  = floor(v + dot(v, C.yyy) );
  vec3 x0 =   v - i + dot(i, C.xxx) ;

// Other corners
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min( g.xyz, l.zxy );
  vec3 i2 = max( g.xyz, l.zxy );

  //   x0 = x0 - 0.0 + 0.0 * C.xxx;
  //   x1 = x0 - i1  + 1.0 * C.xxx;
  //   x2 = x0 - i2  + 2.0 * C.xxx;
  //   x3 = x0 - 1.0 + 3.0 * C.xxx;
  vec3 x1 = x0 - i1 + C.xxx;
  vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
  vec3 x3 = x0 - D.yyy;      // -1.0+3.0*C.x = -0.5 = -D.y

// Permutations
  i = n3mod289(i);
  vec4 p = permute( permute( permute(
             i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
           + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
           + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));

// Gradients: 7x7 points over a square, mapped onto an octahedron.
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
  float n_ = 0.142857142857; // 1.0/7.0
  vec3  ns = n_ * D.wyz - D.xzx;

  vec4 j = p - 49.0 * floor(p * ns.z * ns.z);  //  mod(p,7*7)

  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_ );    // mod(j,N)

  vec4 x = x_ *ns.x + ns.yyyy;
  vec4 y = y_ *ns.x + ns.yyyy;
  vec4 h = 1.0 - abs(x) - abs(y);

  vec4 b0 = vec4( x.xy, y.xy );
  vec4 b1 = vec4( x.zw, y.zw );

  //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
  //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));

  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;

  vec3 p0 = vec3(a0.xy,h.x);
  vec3 p1 = vec3(a0.zw,h.y);
  vec3 p2 = vec3(a1.xy,h.z);
  vec3 p3 = vec3(a1.zw,h.w);

//Normalise gradients
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;

// Mix final noise value
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m * m;
  return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
                                dot(p2,x2), dot(p3,x3) ) );
  }

shaders.noise4d

Four-dimensional simplex noise: snoise(vec4).

GLSL source
//
// Description : Array and textureless GLSL 2D/3D/4D simplex
//               noise functions.
//      Author : Ian McEwan, Ashima Arts.
//  Maintainer : ijm
//     Lastmod : 20110822 (ijm)
//     License : Copyright (C) 2011 Ashima Arts. All rights reserved.
//               Distributed under the MIT License. See LICENSE file.
//               https://github.com/ashima/webgl-noise
//

vec4 mod289(vec4 x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0; }

float mod289(float x) {
  return x - floor(x * (1.0 / 289.0)) * 289.0; }

vec4 permute(vec4 x) {
     return mod289(((x*34.0)+1.0)*x);
}

float permute(float x) {
     return mod289(((x*34.0)+1.0)*x);
}

vec4 taylorInvSqrt(vec4 r)
{
  return 1.79284291400159 - 0.85373472095314 * r;
}

float taylorInvSqrt(float r)
{
  return 1.79284291400159 - 0.85373472095314 * r;
}

vec4 grad4(float j, vec4 ip)
  {
  const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
  vec4 p,s;

  p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
  p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
  s = vec4(lessThan(p, vec4(0.0)));
  p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;

  return p;
  }

// (sqrt(5) - 1)/4 = F4, used once below
#define F4 0.309016994374947451

float snoise(vec4 v)
  {
  const vec4  C = vec4( 0.138196601125011,  // (5 - sqrt(5))/20  G4
                        0.276393202250021,  // 2 * G4
                        0.414589803375032,  // 3 * G4
                       -0.447213595499958); // -1 + 4 * G4

// First corner
  vec4 i  = floor(v + dot(v, vec4(F4)) );
  vec4 x0 = v -   i + dot(i, C.xxxx);

// Other corners

// Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
  vec4 i0;
  vec3 isX = step( x0.yzw, x0.xxx );
  vec3 isYZ = step( x0.zww, x0.yyz );
//  i0.x = dot( isX, vec3( 1.0 ) );
  i0.x = isX.x + isX.y + isX.z;
  i0.yzw = 1.0 - isX;
//  i0.y += dot( isYZ.xy, vec2( 1.0 ) );
  i0.y += isYZ.x + isYZ.y;
  i0.zw += 1.0 - isYZ.xy;
  i0.z += isYZ.z;
  i0.w += 1.0 - isYZ.z;

  // i0 now contains the unique values 0,1,2,3 in each channel
  vec4 i3 = clamp( i0, 0.0, 1.0 );
  vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
  vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );

  //  x0 = x0 - 0.0 + 0.0 * C.xxxx
  //  x1 = x0 - i1  + 1.0 * C.xxxx
  //  x2 = x0 - i2  + 2.0 * C.xxxx
  //  x3 = x0 - i3  + 3.0 * C.xxxx
  //  x4 = x0 - 1.0 + 4.0 * C.xxxx
  vec4 x1 = x0 - i1 + C.xxxx;
  vec4 x2 = x0 - i2 + C.yyyy;
  vec4 x3 = x0 - i3 + C.zzzz;
  vec4 x4 = x0 + C.wwww;

// Permutations
  i = mod289(i);
  float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
  vec4 j1 = permute( permute( permute( permute (
             i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
           + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
           + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
           + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));

// Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
// 7*7*6 = 294, which is close to the ring size 17*17 = 289.
  vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;

  vec4 p0 = grad4(j0,   ip);
  vec4 p1 = grad4(j1.x, ip);
  vec4 p2 = grad4(j1.y, ip);
  vec4 p3 = grad4(j1.z, ip);
  vec4 p4 = grad4(j1.w, ip);

// Normalise gradients
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
  p0 *= norm.x;
  p1 *= norm.y;
  p2 *= norm.z;
  p3 *= norm.w;
  p4 *= taylorInvSqrt(dot(p4,p4));

// Mix contributions from the five corners
  vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
  vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4)            ), 0.0);
  m0 = m0 * m0;
  m1 = m1 * m1;
  return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
               + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;

  }

shaders.pi

PI, TWO_PI and HALF_PI.

GLSL source
//
// Author : Reza Ali
// License : Distributed under the MIT License.
//

#define TWO_PI 6.2831853072
#define PI 3.14159265359
#define HALF_PI 1.57079632679

shaders.random

A hash-based pseudo-random number for a vec2 seed.

GLSL source
//
// Author : Patricio Gonzalez Vivo and Jen Lowe
// License : Distributed under the MIT License.
// Source : https://github.com/patriciogonzalezvivo/thebookofshaders
//

float random (in float _x) {
    return fract(sin(_x)*1e4);
}

float random (in vec2 co) {
    return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);
}