Background
This started in 2013 as a college project in Java and JavaFX 2. The goal was to model a real intersection: a main road meeting a side road, with five vehicle signals, four pedestrian signals, and three turn arrows, laid over an aerial photo of the location.
The original had custom JavaFX controls for each signal type, each with its own skin and behaviour classes, built on JFXtras Labs. Signal cycles were driven by Timeline animations. It also had an animated settings menu ("Izbornik") for changing modes and timings, and a pedestrian push button ("Tipkalo").
The original cars were simple squares moving along fixed paths, and their collision handling was mostly unfinished. Years later, I ported the project to the web so it could run in any browser, and used the port to finish what the original never got to: realistic traffic. Since then I have kept extending it with new traffic types, control strategies and analysis tools.
What It Does
The signals
- Full signal plan: red, red-yellow, green and yellow phases, protected turn arrows and pedestrian phases for the whole intersection.
- Three operating modes: main road priority, side road priority, and flashing yellow for when a police officer directs traffic.
- Adjustable timings: click any vehicle signal on the map, or pick it from the menu, and change its green and red times. The rest of the cycle stays in sync, and a timeline diagram previews the change. A safety check rejects timings that would let both roads go at once.
- Pedestrian button (Tipkalo): pressing it brings pedestrians to the kerbs. They get green at the next safe point in the cycle and walk across. Cars turning across a crosswalk stop for anyone still on it and drive on once they've passed.
Ways of controlling the lights
- Fixed cycle: the original behaviour, always running.
- On demand (induction loops): simulated loops in the road detect cars waiting at a red light, and the cycle only runs when someone is waiting. The loops can be shown on the map.
- Actuated: green is extended while cars keep arriving and cut short when the road is empty.
- Longest queue: the road with more waiting cars gets the green.
- Bus priority: like longest queue, but green is held or called early so an approaching bus doesn't have to wait.
- Learned: a policy trained with reinforcement learning (Q-learning) that decides each second whether to keep the green or switch.
- Ambulance: send one in and the signals clear the other road and give it green until it is through.
The traffic
- Vehicle mix: cars, vans, buses, lorries, motorbikes and bicycles, each with its own size, speed and acceleration, and with brake lights and turn indicators.
- Individual drivers: each driver has their own top speed, reaction time, following gap and willingness to drive through a yellow.
- Traffic density: sliders set how many cars approach from each direction and how many pedestrians arrive per minute.
- Ready-made scenarios: quiet night, rush hour, event on the side road, rainy morning and school zone, each setting the traffic, weather and control in one click.
- Conditions: a day-night cycle with rush hours, a night mode with headlights, rain that makes cars brake earlier and take bends slower, and breakdowns that block a lane with hazard lights until the driver gets going again.
Watching and measuring
- Live statistics: cars per minute, waiting times and queue lengths for each approach, with sparklines, plus pedestrian waiting times.
- Charts and export: whole-run charts of flow, queue and waiting time, and CSV export of the results.
- Level of service: each strategy gets an A-F grade from its average delay, like a real traffic study.
- Fuel and CO₂ estimate: a simple physical model counts fuel for idling, accelerating and driving, so you can see what stop-and-go traffic costs.
- Strategy comparison: run the same traffic under every control strategy, side by side, and see average wait, longest wait, throughput, queue length, pedestrian and bus waiting, and CO₂ per car.
- Best timings search: the app simulates many signal timings in the background to find the green split and cycle length with the shortest delay, then checks the winner on separate runs and compares it with Webster's classic formula.
- Replay: rewind and scrub through the last minute, then jump back to live.
- Follow a car: click a car to see its speed and why it is stopped (red light, queue, giving way, crosswalk).
- Debug view: shows conflict zones and why each car is waiting.
Using it
- Save and share: every setting is stored in the page link, so a scenario can be shared by URL, and a whole set-up, including timings, can be saved to and loaded from a file.
- Bilingual interface: Croatian and English, switchable on the fly.
- Works anywhere: desktop, fullscreen and phones. The scene scales to the window, and on small screens the controls move below the map. It installs as an app and works offline.
- Accessible: keyboard shortcuts for the main actions, screen-reader announcements when the signals change, and reduced-motion support.
How the Traffic Works
The most work in the port went into making the traffic behave believably.
- Driving on routes: each car follows a route traced on the aerial photo, sampled every pixel. A speed profile makes cars brake before tight corners, accelerate smoothly, and queue behind each other.
- Detecting conflicts from the geometry: where two routes cross or merge is found automatically from the road layout, rather than being entered by hand. Right-of-way follows traffic rules: straight on before right turns, and right turns before left turns.
- Deciding at the stop line: each car decides once, at its stop line, whether it can get all the way through the junction. If so, it reserves the conflict zones on its path and releases them as it clears each one. Otherwise, it waits. This prevents cars from getting stuck in the middle of the intersection or blocking a crosswalk.
- Gap acceptance and zip merging: a car that has to give way checks whether oncoming traffic is about to arrive. After waiting long enough, it goes as soon as the conflict zone is physically free, so it can't be held up forever. Where two streams join the same exit road, they take turns.
- Pedestrian safety: cars stop before a crosswalk while someone still has to cross their lane. Pedestrians wait at the kerb if a car is already on the crosswalk or too close to stop.
Engineering
- Written in TypeScript with no UI framework. The scene is a single SVG drawn over the photo, built with Vite and deployed on Netlify.
- The simulation logic is pure TypeScript with no DOM access, kept separate from rendering. This makes it fully testable, and the same code runs in web workers for the strategy comparison and the timing search.
- A Vitest suite of about 130 tests covers the signal sequences and the traffic and pedestrian behaviour. Long seeded runs of the real intersection check that cars never overlap or hit pedestrians, that the main and side roads never have green together under any control strategy, that traffic never gridlocks, and that the same seed always gives exactly the same run.
- Continuous integration runs type-checking, formatting, tests and the production build on every change.
- The rewrite kept the original's positions, timings and terminology, so it behaves like the 2013 version, only finished.





