An online version of the one-street traffic model, all in pure JS

18 February 2023 The Traffic Model

What is this?

We’re testing if we can run models directly in the browser without the user having to download anything or have to contact their IT. There are two options: run the model on a server and access it via a browser or run the model directly in the browser on the user’s computer.

Running the model on a server means we can use any programming language, but it also means paying for server and bandwidth costs long-term and the user may have a harder time real-time interacting with the model.

Running the model directly in the browser on the user’s computer is cost-effective, but it means writing the model in JavaScript which can be challenging.

This work

I wanted to see what was possible using JavaScript for an agent-based model, so I used agentscript to recreate the one-way traffic model from NETLOGO.

Agentscript has many benefits:

  1. Most functions are named and work much like in NETLOGO
  2. There is a clear separation between the view and the model components. This sounds strange given that NETLOGO quite famously does not do that (pxcolor is a significant variable in many models’ logic) but it mostly works
  3. Comes with a good host of utility objects for the usual operations (for example, find neighbors in a grid)

It has some quirks, does not support nodeJS but works with deno, and suffers from the mapping to NETLOGO not being 100% consistent (patch-ahead is toroidal in NETLOGO but not here) but overall it’s a great piece of software and I thought it was very easy to port the model.

The biggest challenge was getting used to the JavaScript ecosystem, including CSS, HTML, web-packing, and minifying. However, this is a learning experience and should get easier with time.

In the end, the combination of tools I used was:

  • Agentscript for the modeling part
  • chart.js for the updating line charts
  • plain javascript for the sliders and keeping the ui in place
  • deno for running the model offline
  • esbuild for bundling

An online run

If everything goes as planned, you should see the model sliders below this paragraph. You can change the number of cars and speed limit before starting the simulation. Once it starts, you can change the speed limit but not the number of cars.

It’s interesting to note that a speed limit of around 0.29 tends to make traffic faster for everyone. Even more intriguing, once the speed limit is kept low long enough and cars are well spaced out, you can raise the speed limit and the entire car system will run smoothly without any more traffic jams.

Although I’m not completely satisfied with the presentation, it works. The presentation isn’t as centered as I’d like, it was challenging to add custom svg images for cars (which is why they’re circles), and the default javascript sliders aren’t very visually appealing. Additionally, there’s limited interactivity and no way to click on individual agents to see their properties.

Running the model offline

Running a model in a browser is great for visualization, but it’s limited for more advanced uses. We want to be able to run simulations and optimizations, which can’t be done efficiently in a browser.

Thankfully, it’s easy to run parameter sweeps outside of a browser. The code is straightforward and simple to write.

import TrafficBasicModel from "./traffic_model.js";
//this function runs the model once
function one_run(speed_limit: number) : number {
  const model = new TrafficBasicModel();
  model.setup();
  model.speed_limit = speed_limit
  for (let i = 0; i < 500; i++) {
    model.step();
  }
  return model.averageSpeed
}

//then we just run the model 1,000 times and store the 
//results a csv
let csv: string = "limit,speed" + "\n"
for(let limit = 1; limit>=0; limit= (limit -0.01)) {
    limit = Math.round(limit * 100) / 100
    console.log(limit)
    for(let run=0; run<=10; run++){
        const sample =  Math.round(one_run(limit)*1000)/1000
        csv = csv + `${limit},${sample}` + "\n"
    }
}
console.log(csv)
await Deno.writeTextFile('./sweep.csv', csv);

Then we can just analyse the csv on our computer anyway we please:

Notice that there is a bit of noise, but clearly for the “median” run some speed limit is better than none.

Lessons learned

JS can be a great choice for building models, and it comes with a range of libraries for various purposes, such as optimization, statistics, linear programming, and visualization. Although it is fast for offline use, it’s slower than Java but faster than Python.

Dynamic typing in JS can lead to mistakes that are avoided in statically typed languages. For future projects, using TypeScript might be a better choice to avoid frustration.

Agentscript and chart.js for visualizations are okay, but exploring D3 might be a good idea. Improving CSS skills will be necessary to make the models look polished.

Raw JavaScript may not be suitable for building a lot of sliders, and considering a tool like React might be necessary. Not looking forward to that.