I write about my quantitative explorations in visualisation, data science, machine and deep learning here, as well as other random musings.

For more about me and my other interests, visit playgrd or socials below


Generating a Strange Attractor in three.js

Art and math - An experiment with the lorenz function in three.js

Quantitative finance, data science, art, nature…

You would think that these are totally separate fields, but the deeper down the rabbit hole you go, the more you realise how much they have in common. Concepts relating to randomness and patterns, fractals, golden ratios span all these fields.

As I work in a finance field, dabble in data science, and love both illustration and generative art, these commonalities never fail to amaze me.

More on this subject another day, but for now, a tutorial on how to do a visualisation using three.js and the Lorenz equation.

Edward Lorenz, developed the equation while modelling the weather, and is also credited with the ‘butterfly effect’ - which is basically an insight into how small changes in nature can have huge consequences.

Does the flap of a butterfly’s wings in Brazil set off a tornado in Texas? - Title of Edward Lorenz’s 1972 paper

These are all related to this concept of ‘Strange Attractors’, of which the Lorenz equation or function is one. So let’s try generating it.

First, we set up the scene, camera, lights, and the renderer to render them to the screen.

// Setting up the scene
var scene = new THREE.Scene();

// Setting up a camera
var camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 0.1, 50 );
camera.position.z = 30;

// Setting up the renderer. This will be called later to render scene with the camera setup above
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xF70051, 1 );
document.body.appendChild( renderer.domElement );

// Setting up a light
var light = new THREE.PointLight( '#9BC995', 1, 1000 );
light.position.set( 0, 0, 0 );
scene.add( light );

We then generate the points plotting out the familiar butterfly shape of the Lorenz Equation.

function lorenz(){

    var arrayCurve=[];

    var x = 0.01, y = 0.01, z = 0.01;
    var a = 0.9;
    var b = 3.4;
    var f = 9.9;
    var g = 1;
    var t = 0.001;
    for (var i=0;i<100000;i++){

    var x1 = x;
    var y1 = y;
    var z1 = z;
    x = x - t*a*x +t*y*y -t*z*z + t*a*f; 
    y = y - t*y + t*x*y - t*b*x*z + t*g;
    z = z - t*z + t*b*x*y + t*x*z;
    arrayCurve.push(new THREE.Vector3(x, y, z).multiplyScalar(1));   
    return arrayCurve;

These are fed into the geometry of a cloud of points.

// Array curve holds the positions of points generated from a lorenz equation; lorenz function below generates the points and returns an array of points
var arrayCurve = lorenz();

// Generating the geometry
var curve = new THREE.CatmullRomCurve3(arrayCurve);
var geometry = new THREE.Geometry();
geometry.vertices = curve.getPoints(100000);

// Generating a cloud of point
var pcMat = new THREE.PointsMaterial();
pcMat.color = new THREE.Color(0x5555ff);
pcMat.transparent = true;
pcMat.size = 0.05;
pcMat.blending = THREE.AdditiveBlending;
pc = new THREE.Points(geometry, pcMat);
pc.sizeAttenuation = true;
pc.sortPoints = true;

Now we illustrate the actual butterfly effect, by changing the coefficients in the Lorenz equation slightly and randomly for each frame.

var render = function () {

    renderer.render( scene, camera );
    requestAnimationFrame( render );

    //Varying the points on each frame
    var count = 0;
    var geometry = pc.geometry;
    var a = 0.9+Math.random()*6;
    var b = 3.4+Math.random()*7;
    var f = 9.9+Math.random()*8;
    var g = 1+Math.random();
    var t = 0.001;

        v.x = v.x - t*a*v.x +t*v.y*v.y -t*v.z*v.z + t*a*f; 
        v.y = v.y - t*v.y + t*v.x*v.y - t*b*v.x*v.z + t*g;
        v.z = v.z - t*v.z + t*b*v.x*v.y + t*v.x*v.z;
    geometry.verticesNeedUpdate = true;

    group.rotation.x += 0.01;
    group.rotation.y += 0.02;
    group.rotation.z += 0.01;

And that’s it! :)

The actual simulation of the Lorenz equation and the butterfly effect can be found here; and the code here.


AI and UIs
Listing NFTs
Extracting and Processing Wikidata datasets
Extracting and Processing Google Trends data
Extracting and Processing Reddit datasets from PushShift
Extracting and Processing GDELT GKG datasets from BigQuery
Some notes relating to Machine Learning
Some notes relating to Python
Using CCapture.js library with p5.js and three.js
Introduction to PoseNet with three.js
Topic Modelling
Three.js Series - Manipulating vertices in three.js
Three.js Series - Music and three.js
Three.js Series - Simple primer on three.js
HTML Scraping 101
(Almost) The Simplest Server Ever
Tweening in p5.js
Logistic Regression Classification in plain ole Javascript
Introduction to Machine Learning Right Inside the Browser
Nature and Math - Particle Swarm Optimisation
Growing a network garden in D3
Data Analytics with Blender
The Nature of Code Ported to Three.js
Primer on Generative Art in Blender
How normal are you? Checking distributional assumptions.
Monte Carlo Simulation of Value at Risk in Python
Measuring Expected Shortfall in Python
Style Transfer X Generative Art
Measuring Market Risk in Python
Simple charts | crossfilter.js and dc.js
d3.js vs. p5.js for visualisation
Portfolio Optimisation with Tensorflow and D3 Dashboard
Setting Up a Data Lab Environment - Part 6
Setting Up a Data Lab Environment - Part 5
Setting Up a Data Lab Environment - Part 4
Setting Up a Data Lab Environment - Part 3
Setting Up a Data Lab Environment - Part 2
Setting Up a Data Lab Environment - Part 1
Generating a Strange Attractor in three.js
(Almost) All the Most Common Machine Learning Algorithms in Javascript
3 Days of Hand Coding Visualisations - Day 3
3 Days of Hand Coding Visualisations - Day 2
3 Days of Hand Coding Visualisations - Day 1
3 Days of Hand Coding Visualisations - Introduction