WebGL 3d dynamical systems visualization
Clone or download
Jamie Portsmouth
Jamie Portsmouth Update min.js
Latest commit 9ddc484 Jan 13, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs Create thumbnails to optimize README page loading speed Jan 4, 2019
js Update min.js Jan 13, 2019
scripts Adding GIF animation rendering facilities. Jan 1, 2019
shaders Adding "teleport" facility for implementing e.g. periodic bounding co… Jan 13, 2019
LICENSE initial commit Sep 10, 2018
README.md Better teleport code example. Jan 13, 2019
debug.html Minify. Nov 18, 2018
index.html Cleanup. Nov 20, 2018



Fibre is a WebGL application for visualizing and coding 3d vector fields and dynamical systems. A number of presets with well-known or interesting dynamical systems are provided as below (click to launch). New vector fields can be authored in the code editor, and shared via an HTML link with the embedded code.

In three (or more) dimensions, a non-linear continuous dynamical system may exhibit chaos, which is characterised by sensitive dependence of the solution trajectories to the initial conditions, and evolution governed by a so-called "strange attractor". Probably the most famous example is the Lorenz system, initially discovered as a simplified model of atmospheric convection:

whose solution curves (x(t), y(t), z(t)), starting from any initial point (x(0), y(0), z(0)), tend to the Lorenz strange attractor.

In general, a three-dimensional continuous dynamical system is a system of first order ordinary differential equations (ODE) of the form below, where the functions on the right hand side define a vector field giving the velocity vector of the dynamical system at each point in space (and, optionally, time):

In Fibre, the code editor on the left specifies this velocity vector field via the GLSL function vec3 velocity(vec3 p, float t) . Additionally, a color at each point in space must be specified via vec3 color(vec3 p, float t). The system then traces the solution curves of the ODE (using the Runge-Kutta method), starting from a grid of points covering the "initial conditions box" positioned in the viewport. These curves are rendered as colored tubes. (Note, the tubes are not rendered as geometry, they consist of many individual line segments, which are progressively rendered and blended over a number of iterations in order to converge to the final image).

UI controls:

  • left-click mouse to rotate, right-click mouse to pan camera
  • hover over the initial conditions box and left-click to drag it around, or hover over the box corners and left-click to resize the box dimensions. The box extents can also be edited directly in the Integrator rollout of the UI.
  • the first (green) ☰ button hides/shows the code editor, in which the velocity vector field and color field are defined as GLSL functions.
  • editing the GLSL code immediately updates the shader -- if there is an error, this will be copied to the viewport, and rendering terminated
  • click on values in the code editor to pop up a slider which scrubs the value
  • click on colors (e.g. rgb(255, 0, 0)) in the code editor to pop up a color wheel
  • The second (blue) ☰ button generates a data URL which can be used to save and share the current vector field. Note that the URL itself contains the full state of the application, including the GLSL code, all the UI settings, and the camera and initial conditions box. So this link can be used to share the exact state of the application with anyone else (e.g. on Twitter).
  • AWSD keys to fly
  • C key to frame camera on the initial conditions box
  • P key to capture a screenshot of the current render in a new browser window
  • H key to hide/show the sidebar UI
  • F11 key to enter/exit fullscreen mode

GLSL format

The GLSL code defining the vector field must have the following form:

// Mandatory function giving the vector field x, y, z components as a function of 3D spatial position.
// Optionally, these can also be a function of the supplied integration time t (generating a "non-autonomous" system)
vec3 velocity(vec3 p, float t)
    // example code (Thomas attractor)
    vec3 v;
    float x = p.x;
    float y = p.y;
    float z = p.z;
    const float b = 0.15; // (value is draggable in the code editor)
    v.x = -b*x + sin(y);
    v.y = -b*y + sin(z);
    v.z = -b*z + sin(x);
    return v;

// Mandatory function giving the diffuse color components associated with each point in space,
// and/or integration time.
vec3 color(vec3 p, float t)
    // example code
    const vec3 colLo = vec3(254,45,73) / 255.0;
    const vec3 colHi = vec3(5,138,255) / 255.0;
    float lerp = t/(float(abs(5.0))+t);
    return (1.0-lerp)*colLo + lerp*colHi;

// Optional function (called if the word "teleport" appears anywhere in the code)
// which can be used to displace the current point in the integration trajectory an arbitrary amount, 
// without drawing a line between the previous position and the updated position (thus introducing a broken line).
// The function should determine whether such a "teleportation" event is required, 
// given the current integration point p and the previous point pprev, 
// and if so displace p in-place and return true (otherwise, return false).
// This can be used to implement, for example, periodic boundary conditions, as shown below.
bool teleport(inout vec3 p, in vec3 pprev)
    // example code
    const float Xmin = 0.0; const float Xmax = 1.0; const float DX = Xmax - Xmin;
    const float Ymin = 0.0; const float Ymax = 1.0; const float DY = Ymax - Ymin;
    const float Zmin = 0.0; const float Zmax = 1.0; const float DZ = Zmax - Zmin;
    const float eps = 1.0e-2 * min(min(DX, DY), DZ);
    if (p.x < Xmin + eps) { p.x += DX - eps; return true; }
    if (p.x > Xmax - eps) { p.x -= DX + eps; return true; }
    if (p.y < Ymin + eps) { p.y += DY - eps; return true; }
    if (p.y > Ymax - eps) { p.y -= DY + eps; return true; }
    if (p.z < Zmin + eps) { p.z += DZ - eps; return true; }
    if (p.z > Zmax - eps) { p.z -= DZ + eps; return true; }
    return false;

Integration parameters

  • gridSpace: the spacing between start points within the initial conditions box, relative to the box maximum extents
  • tubeWidth: the radius of the rendered solution tubes, relative to the box maximum extents
  • integrationTime: the total time to integrate over
  • maxTimeSteps: the number of timesteps into which the integrationTime is broken into
  • xmin: (etc.) initial conditions box bounds
  • clipToBounds: renders only the portion of the solution curves which lies within the initial conditions box
  • integrateForward: if selected, integrate over positive times i.e. the time interval [0, integrationTime], otherwise integrate over the time interval [-integrationTime/2, integrationTime/2]
  • enableBounds: if disabled, the initial conditions box is locked (allowing the camera to be moved within the box)

Rendering parameters

  • showBounds: whether to show the initial conditions box bounds
  • exposure: controls overall brightness of image
  • gamma: controls gamma correction factor of image
  • contrast: controls image contrast
  • saturation: controls image saturation
  • fov: change the camera field-of-view
  • hairShader: use a simple hair shading model for the specular component (otherwise uses the Blinn-Phong model)
  • specShine: controls size of tube specular highlight
  • specColor: the color of the specular highlight (blended with the diffuse tube color defined in the code)
  • depthTest: controls whether tubes are opaque or blended

Advanced parameters

  • rayBatch: the number of ray segments to render in one iteration (higher means convergence in less iterations, but slower iterations)
  • maxIterations: the max iteration count, beyond which the render terminates. Increase this to improve the final image quality.
  • subtractiveColor: treat the colors as absorption coefficients
  • bgColor: change color of background (probably use in conjunction with subtractiveColor)
  • light1_color: the color of the first light, used to modulate the diffuse reflectance from the tubes
  • light2_color: the color of the second light, used to modulate the diffuse reflectance from the tubes
  • light1_dir: the direction of the first light, used to modulate the diffuse reflectance from the tubes
  • light2_dir: the direction of the second light, used to modulate the diffuse reflectance from the tubes
  • dashes: enable animated dashes showing the direction of flow (must use in conjunction with depthTest)
  • dash_spacing: space between animated dashes, relative to gridSpace
  • dash_size: fractional size of dash, in [0, 1]
  • dash_speed: rate of dash motion, in dashes per second
  • RECORD: start/stop recording a GIF of the viewport interaction
  • RECORD PERIOD: record a GIF of one dash period (for repeating GIFs of the animated dash motion)
  • record_realtime: whether to record the real-time interaction to the GIF, or at the maximum frame-rate
  • RENDER ANIM: start recording a GIF of a number of automatically rendered animated frames (the number specified via anim_frames). Each frame renders for the number of iterations specified via maxIterations. The camera can optionally (via anim_enable_turntable) be moved in a turntable motion through a specified number of degrees anim_turntable_degrees during the animation. The uniform float variable animFraction is available in the shader, which equals the number of elapsed frames divided by the total number of frames. At the end of the animation, a GIF is rendered (which may take some time) and saved to the downloads folder with a standard name.
  • CANCEL ANIM: cancel the current animation recording
  • anim_frames: how many animation frames to render in the generated GIF
  • anim_enable_turntable: whether to move the camera in a turntable sweep motion during the animation
  • anim_turntable_degrees: if turntable is enabled, how many degrees to rotate through