Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Traffic Simulator Banner

Traffic Simulator is a full-stack academic project that models vehicle behavior at a two-street signalized intersection. A Julia backend runs a continuous-space agent-based simulation, a Genie API exposes independently addressable simulation instances, and a React interface visualizes vehicles, traffic lights, controls, and velocity metrics.

The project explores how local rules - vehicle following, traffic-light response, acceleration, braking, and periodic road boundaries - produce observable traffic patterns at the system level.

Note

This is a public educational project. The model is intended for experimentation and software-learning purposes; it is not a calibrated transportation model, a road-safety tool, or a traffic-engineering decision system.


Overview

The simulator represents a simplified intersection formed by two perpendicular avenues:

  • West Avenue (av1) carries vehicles horizontally along the positive X direction.
  • North Avenue (av2) carries vehicles vertically toward decreasing Y coordinates.
  • Each avenue contains two fixed lanes selected randomly when vehicles are created.
  • One traffic light controls each avenue.
  • Signal phases are offset so one avenue begins with a permissive phase while the other begins with a restrictive phase.
  • Vehicles detect the closest car and traffic light ahead, choose a target velocity, smooth the transition, and move through a periodic continuous space.
  • The browser advances the backend one step per polling request and renders the returned state as SVG elements.
  • Plotly records average speed by avenue and the combined speed sum for later comparison.

The implementation was inspired by Uri Wilensky's NetLogo Traffic Intersection model and adapted into a distributed Julia-and-React architecture.


Preview

Traffic Simulator interface showing controls, a signalized intersection, vehicles, and velocity charts

The interface provides:

  • Setup, start, and stop controls.
  • A simulation-speed slider.
  • Independent vehicle-count controls for North Avenue and West Avenue.
  • An SVG intersection with live vehicle and signal positions.
  • A Plotly chart generated when the run is stopped.

Academic Context

This project was developed collaboratively as a university exercise in computational modeling and multi-agent systems.

The main learning goals were to:

  • Represent traffic participants as autonomous agents.
  • Model a signalized intersection with coordinated phase timing.
  • Apply local perception rules to vehicle movement.
  • Separate simulation logic from visualization.
  • Expose simulation state through an HTTP API.
  • Connect Julia and JavaScript in a full-stack workflow.
  • Visualize position and velocity data in the browser.
  • Compare aggregate behavior under different vehicle densities and update rates.

Important

The original project notes that development was performed collaboratively and synchronously. Commit counts should not be interpreted as a complete measure of each contributor's work.


Core Capabilities

Capability Implementation Status
Continuous Traffic Model Agents.jl ContinuousSpace with two-dimensional positions and velocities Implemented
Vehicle Agents Street assignment, orientation, perception, target-velocity selection, smoothing, and movement Implemented
Traffic-Light Agents Green, yellow, and red states driven by a synchronized counter Implemented
Two-Lane Streets Two fixed coordinate lanes per avenue with randomized initial placement Implemented
Periodic Traffic Flow Vehicles wrap across the continuous-space boundaries Implemented
Simulation Instances UUID-addressed models stored independently in the API process Implemented in memory
Interactive Controls Setup, start, stop, update speed, and vehicle-count sliders Implemented
SVG Visualization Live intersection, vehicle sprites, and state-dependent signal sprites Implemented
Velocity Analytics Per-avenue averages and a combined velocity sum plotted with Plotly Implemented
Automated Tests Unit, API, and browser tests Not included

System Architecture

The project separates the numerical model, HTTP transport, and visual client.

flowchart TD
    U["User Controls"] --> R["React and Vite Client"]
    R -->|"POST setup"| G["Genie API"]
    R -->|"GET next step"| G
    G --> J["Agents.jl Model"]
    J --> G
    G -->|"JSON agents"| R
    R --> V["SVG Scene and Plotly Chart"]
Loading

Component Responsibilities

Component Responsibility
simple.jl Defines agent types, perception rules, movement logic, signal timing, initial positions, and the simulation model.
webapi.jl Creates simulation instances, advances models, serializes agent state, configures CORS, and starts Genie.
fe/src/App.jsx Manages controls, API polling, React state, SVG rendering, speed sampling, and Plotly output.
fe/public/ Stores vehicle and traffic-light sprites used by the SVG scene.
image.png Provides a repository-level screenshot of the running interface.

Technology Stack

Area Technologies
Simulation Language Julia
Agent-Based Modeling Agents.jl
Vector Representation StaticArrays and SVector
Randomization Julia Random and MersenneTwister
Backend Framework Genie
HTTP and Serialization Genie JSON renderer, Genie Requests, HTTP.jl
Instance Identification UUIDs
Frontend React 18, JavaScript, JSX
Development Server and Build Vite 5
Interface Components AWS Amplify UI React
Visualization SVG and image sprites
Analytics Plotly.js
Code Quality ESLint with React and Hooks plugins

Note

JavaScript dependencies are locked through package-lock.json. The repository does not currently include Julia Project.toml or Manifest.toml files, so Julia package versions are not pinned.


Repository Structure

Traffic-Simulator-main/
├── README.md                 # Project documentation
├── LICENSE                   # MIT license
├── image.png                 # Interface screenshot
├── simple.jl                 # Agent model and simulation rules
├── webapi.jl                 # Genie HTTP API
├── Reflexiones/              # Academic reflection documents
└── fe/
    ├── package.json          # Frontend scripts and dependencies
    ├── package-lock.json     # Locked npm dependency graph
    ├── vite.config.js        # Vite React configuration
    ├── public/               # Car and traffic-light image assets
    └── src/
        ├── App.jsx           # Controls, polling, SVG, and analytics
        ├── App.css           # Component-level styles
        ├── index.css         # Global Vite template styles
        └── main.jsx          # React application entry point

Simulation Model

Continuous Space

The backend creates a two-dimensional continuous environment with the following configuration:

Parameter Value Meaning
Extent (28, 15) Width and height of the simulation coordinate system.
Spacing 0.5 Spatial indexing resolution used by Agents.jl.
Periodic true Agents leaving one boundary re-enter from the opposite side.
Scheduler Schedulers.fastest Uses Agents.jl's low-overhead scheduler.
Agent Union Car and stopLight Both types coexist in the same model.
Movement interval 0.4 Value passed to move_agent! for each vehicle step.

Periodic boundaries create a closed traffic loop. Vehicles are not deleted after crossing the visible road; they wrap around and continue circulating.

Agent Types

Both agent types extend ContinuousAgent{2, Float64}, which provides an identifier, position, and velocity.

Car

Field Type Purpose
id Inherited Unique Agents.jl identifier.
pos Inherited Continuous (x, y) location.
vel Inherited Two-dimensional velocity vector.
accelerating Bool Reserved state flag; initialized but not currently used by the controller.
street Streets Associates the vehicle with av1 or av2.
orientation Float64 Determines the movement direction through trigonometric projection.

Traffic Light

Field Type Purpose
id Inherited Unique Agents.jl identifier.
pos Inherited Fixed location near the intersection.
vel Inherited Zero vector; traffic lights do not move.
status LightColor Current green, yellow, or red state.
time_counter Int Current tick inside the signal cycle.
street Streets Avenue controlled by the signal.

Enumerations

@enum LightColor green yellow red
@enum Streets av1 av2

Enumerations keep the simulation state constrained to known values and are serialized as readable values for the frontend.


Intersection Geometry

Vehicle Initialization

Avenue Model Name Direction Fixed Lane Coordinates Random Spawn Range Initial Velocity
West Avenue av1 Positive X y = 7 or y = 8 x = 5.0...20.0 in 0.5 increments (0.1, 0.0)
North Avenue av2 Decreasing Y x = 13 or x = 14 y = 0.0...10.0 in 0.5 increments (0.0, 0.1)

The lane is selected randomly when each vehicle is created. Vehicles remain in their assigned lane; the current implementation does not perform lane changes.

Traffic-Light Initialization

Controlled Avenue Position Initial State Initial Counter
North Avenue (av2) (12.0, 3.5) Green 0
West Avenue (av1) (16.3, 8.5) Red 60

The 60-tick counter offset places the two lights in opposite halves of the same 120-tick cycle.


Traffic-Light State Machine

The signal constants are:

green_duration = 45
yellow_duration = 15
cycle_length = 2 * (green_duration + yellow_duration) # 120 ticks

Each traffic-light agent increments its counter once per simulation step.

stateDiagram-v2
    [*] --> Green
    Green --> Yellow: After 45 ticks
    Yellow --> Red: After 15 ticks
    Red --> Green: After 60 ticks
Loading
Counter Range State Duration
1...45 Green 45 ticks
46...60 Yellow 15 ticks
61...120 Red 60 ticks

When the counter exceeds 120, it resets to 1. Because the second light begins at 60, its red interval aligns with the first light's green-and-yellow interval.

Note

Signal time is measured in model ticks, not real-world seconds. Wall-clock duration depends on the browser polling rate and request latency.


Vehicle Perception

Each car performs two local searches before selecting its next velocity.

Closest Car Ahead

closest_car_ahead filters nearby agents using four conditions:

  1. The neighboring agent must also be a Car.
  2. Both cars must belong to the same avenue.
  3. Both cars must occupy the same fixed lane coordinate.
  4. The neighbor must be ahead according to the avenue's travel direction.
Avenue Search Radius Same-Lane Test Ahead Test Distance
av1 20.5 Equal Y Neighbor X is greater neighbor.x - car.x
av2 1.8 Equal X Neighbor Y is smaller car.y - neighbor.y

The function returns both the closest matching vehicle and its distance. If no vehicle qualifies, it returns nothing and Inf.

Closest Traffic Light Ahead

closest_light_ahead searches within a radius of 20.0 and selects a stopLight associated with the same avenue.

The horizontal avenue uses direct X-distance. The vertical avenue applies a custom coordinate adjustment so the continuous model aligns with the rendered stop line.

Important

Perception is intentionally local and rule-based. The model does not include sensors, occlusion, driver reaction-time distributions, vehicle length, or probabilistic behavior.


Vehicle Decision Logic

Every car executes the same priority-based controller.

flowchart TD
    P["Perceive nearest car and light"] --> C{"Car ahead is closer?"}
    C -->|"Yes"| F["Follow: accelerate, stop, or reverse"]
    C -->|"No"| L{"Red or yellow light ahead?"}
    L -->|"Yes"| B["Brake or reverse near stop line"]
    L -->|"No"| A["Accelerate"]
    F --> S["Smooth velocity and move"]
    B --> S
    A --> S
Loading

Priority 1: Vehicle Following

If a car is ahead and closer than the nearest traffic light, distance determines the target behavior.

Ordered Condition Behavior
1.2 <= distance <= 2.5 Select the stop target velocity.
Otherwise, distance below 2.65 on av1 or 2.4 on av2 Select the reverse target velocity.
Any larger distance Select the acceleration target velocity.

Conditions are evaluated in order. The controller uses reverse movement as a corrective response when agents become too close or cross a configured threshold.

Priority 2: Signal Response

If no closer vehicle controls the decision, the car evaluates the nearest signal.

Signal and Distance Behavior
Green Accelerate.
Red or yellow, within the braking interval Select the stop target velocity.
Red or yellow, closer than 1.8 adjusted units Select the reverse target velocity.
No relevant signal Accelerate.

The braking interval extends to 3.5 units on av1 and 8.5 adjusted units on av2.


Velocity Model

The simulation uses a heuristic kinematic controller rather than a calibrated physical model.

For each avenue, the backend calculates three candidate velocities:

  • accelerate: increases motion in the avenue's forward direction.
  • stop: reduces the current speed toward zero.
  • reverse: introduces a limited corrective movement when an agent is too close to an obstacle or stop line.

After selecting a target, the current velocity is blended with it using linear interpolation:

v_next = v_current * (1 - alpha) + v_target * alpha
alpha  = 0.18

Therefore:

v_next = 0.82 * v_current + 0.18 * v_target

This exponential-style smoothing prevents an instantaneous jump from the current velocity to the newly selected target.

Direction is projected with trigonometric terms:

  • Horizontal motion uses cos(orientation).
  • Vertical motion uses sin(orientation).
  • av1 starts with orientation 0.
  • av2 starts with orientation 3pi/2, producing motion toward decreasing Y.

Caution

Acceleration, braking, and reverse formulas were tuned for visual behavior in this simplified coordinate system. Values are not expressed in SI units and should not be interpreted as measured vehicle dynamics.


Backend API

The Genie service runs on http://localhost:8000 by default.

Create a Simulation

POST /simulations
Content-Type: application/json

Request body:

{
  "numCarsN": 3,
  "numCarsO": 2
}

Response structure:

{
  "Location": "/simulations/<uuid>",
  "cars": [
    {
      "id": 3,
      "pos": [12.5, 7.0],
      "vel": [0.1, 0.0],
      "street": "av1",
      "orientation": 0.0
    }
  ],
  "stopLights": [
    {
      "id": 1,
      "pos": [12.0, 3.5],
      "status": "green",
      "time_counter": 0,
      "street": "av2"
    }
  ]
}

The exact serialized objects may include additional agent fields.

Advance a Simulation

GET /simulations/:id

This endpoint:

  1. Retrieves the model from the in-memory instances dictionary.
  2. Executes run!(model, 1).
  3. Separates cars and traffic lights.
  4. Returns the updated agent state.

Response structure:

{
  "cars": [],
  "stopLights": []
}

Important

GET /simulations/:id is not a read-only operation: every request advances the model by one tick. Multiple clients polling the same identifier will advance the same model more quickly and may observe interleaved states.

API Example with cURL

curl -X POST http://localhost:8000/simulations \
  -H "Content-Type: application/json" \
  -d '{"numCarsN":3,"numCarsO":2}'

Copy the returned Location and request the next state:

curl http://localhost:8000/simulations/<uuid>

Simulation Instance Lifecycle

Each setup request generates a UUID and stores the corresponding Agents.jl model in a process-level dictionary:

instances[uuid] = model

This design allows several browser sessions to create separate simulations without sharing car positions or signal counters.

The implementation is intentionally lightweight:

  • State exists only in server memory.
  • Restarting Julia removes every simulation.
  • Instances do not currently expire.
  • There is no endpoint for deleting a simulation.
  • The server does not persist run history.
  • Concurrent access is not explicitly synchronized.

Frontend Execution Model

The React client follows three phases.

Setup

Setup sends the selected vehicle counts to POST /simulations and stores:

  • The returned simulation location.
  • Initial vehicle state.
  • Initial traffic-light state.

Start

Start clears previous measurements and creates a JavaScript interval. Each interval callback requests the next state and updates the SVG.

The timer delay is:

poll_interval_ms = 500 / simulation_speed

With a slider range from 1 to 30, the requested interval ranges from 500 ms to approximately 16.7 ms.

Ignoring network and processing delay, the nominal request frequency is:

requests_per_second = 1000 / poll_interval_ms
                    = 2 * simulation_speed

Because one request advances one tick, the browser acts as the simulation clock.

Caution

setInterval does not wait for the preceding fetch to complete. At high speed values, requests can overlap if the backend or network takes longer than the configured interval.

Stop

Stop clears the interval, re-enables setup controls, and builds the Plotly chart from the accumulated samples.


SVG Coordinate Mapping

The browser does not reuse backend coordinates directly as pixels. It applies avenue-specific scaling to align agent positions with the illustrated intersection.

Object Browser X Browser Y
av1 car x * 35 50 + y * 20
av2 car x * 32 y * 34
Traffic light -13 + x * 32 83 + y * 20

The SVG map is composed from green terrain rectangles and gray road rectangles. Vehicle and traffic-light states are rendered with PNG sprites stored in fe/public/.

Traffic-light image selection is state-driven:

red    -> SemRojo.png
yellow -> SemAmarillo.png
green  -> SemVerde.png

Velocity Analytics

For every returned car, the frontend calculates speed magnitude:

speed = sqrt(vx^2 + vy^2)

Samples are grouped by avenue.

Series Calculation Chart Color
West Avenue Sum of av1 speed magnitudes divided by numCarsO Light blue
North Avenue Sum of av2 speed magnitudes divided by numCarsN Light red
Combined Speed Sum of all speed magnitudes across both avenues Black

The first two series are per-avenue averages, while the black series is a total rather than an average. The horizontal axis represents polling samples, not guaranteed wall-clock seconds or simulation time units.

Warning

Selecting zero cars for an avenue causes its average calculation to divide by zero. A production-quality revision should return 0, null, or omit the empty series instead of producing NaN.


Getting Started

Prerequisites

The repository does not declare exact Node.js or Julia engine versions. Vite 5 and the Agents.jl APIs used by the code must be supported by the installed runtimes.

1. Open the Repository

Clone or download this repository, then enter its project directory:

cd Traffic-Simulator-main

2. Install Julia Dependencies

From the project root, start Julia and install the required packages:

using Pkg
Pkg.add(["Agents", "StaticArrays", "Genie", "HTTP"])

Tip

For reproducible development, create a Julia environment with Pkg.activate(".") before adding dependencies, then commit the generated Project.toml and Manifest.toml files.

3. Start the Backend

From the repository root:

julia webapi.jl

Genie should start the API on:

http://localhost:8000

4. Install Frontend Dependencies

Open a second terminal:

cd fe
npm install

5. Start the Frontend

npm run dev

Open the local URL printed by Vite, normally:

http://localhost:5173

6. Run a Scenario

  1. Select the number of cars for each avenue.
  2. Select a simulation speed.
  3. Click Setup to create a backend model.
  4. Click Start to begin polling and rendering.
  5. Observe vehicle and traffic-light behavior.
  6. Click Stop to stop polling and generate the velocity chart.

Important

Start the backend before the frontend and click Setup before Start. The current interface does not provide complete recovery messages for a missing API connection or an uninitialized simulation location.


Available Frontend Commands

Run these commands from fe/:

Command Purpose
npm run dev Starts the Vite development server with hot reload.
npm run build Creates a production frontend bundle.
npm run lint Runs ESLint over the frontend source.
npm run preview Serves the generated production bundle locally.

Contributors and Collaboration

The original project documentation attributes the work to:

  • Abigail Pérez García
  • Rodrigo López Guerra

The documented contribution areas include:

Area Documented Work
Intersection and Controls SVG map construction, initial interface controls, simulation-speed control, and vehicle-count configuration.
Vehicle Foundations Initial vehicle-agent definition and movement-rule exploration.
Signal Logic Traffic-light agents, phase counters, state changes, and opposite-phase initialization.
Vehicle Controller Perception, distance rules, target velocities, smoothing, and movement integration.
Scenario Population Vehicles for both avenues and randomized fixed-lane placement.
Analytics Speed magnitude, per-avenue sampling, and Plotly chart generation.

The project documentation emphasizes synchronous collaboration; the contribution table summarizes areas described by the team and is not intended as a commit-by-commit ownership record.


Design Decisions

  • Continuous instead of grid-only movement: vehicle positions and velocities can change by fractional values.
  • Local agent perception: each car reacts only to nearby cars and signals rather than a centralized traffic controller.
  • Periodic boundaries: vehicles recirculate, supporting long-running observation without spawn-and-despawn logic.
  • Opposed signal phases: a single counter offset coordinates the two avenues with minimal state.
  • Heuristic velocity smoothing: linear interpolation produces visually gradual movement changes.
  • Client-driven stepping: the API remains simple while the frontend controls run speed through polling frequency.
  • Instance-specific URLs: UUIDs keep separate setup requests logically isolated.
  • SVG rendering: the intersection remains lightweight and directly driven by React state.
  • Post-run plotting: measurements are accumulated during execution and visualized when the user stops the run.

Known Limitations

The repository is a functional academic prototype with intentionally simplified behavior.

Model Limitations

  • No turning vehicles or turning signals.
  • No pedestrians, crossings, priority vehicles, or traffic incidents.
  • Fixed lanes without lane-changing behavior.
  • Vehicles may spawn at overlapping or very close coordinates.
  • No explicit vehicle length, maximum acceleration, or realistic braking distance.
  • Heuristic reverse motion can create behavior that would not be permitted in real traffic.
  • Signal timing is measured in request-driven ticks rather than physical seconds.
  • Detection rules near periodic boundaries do not fully model wrapped forward distance.
  • Random initialization is not exposed as a reproducible user seed.

API Limitations

  • Simulation state is memory-only and disappears on restart.
  • Instances do not expire and cannot be deleted through the API.
  • Missing or unknown UUIDs do not receive a dedicated domain-level error response.
  • Vehicle counts are not validated server-side.
  • CORS accepts every origin.
  • No authentication, rate limiting, persistence, or concurrency control.
  • A state-changing operation is exposed through GET.

Frontend Limitations

  • API URLs are hardcoded to localhost:8000.
  • Fetch failures are not surfaced through a user-facing error state.
  • Start can be requested without a valid setup response.
  • A zero vehicle count can produce division-by-zero analytics.
  • High simulation speeds can produce overlapping HTTP requests.
  • The SVG uses fixed dimensions and is not fully responsive.
  • Plot time is a sample index rather than normalized simulation time.

Engineering Limitations

  • No Julia environment manifest.
  • No automated unit, integration, API, or browser tests.
  • No continuous-integration workflow.
  • No structured backend logging or performance instrumentation.

Testing Recommendations

A future automated test suite should cover:

Simulation Unit Tests

  • Traffic-light transitions at ticks 45, 46, 60, 61, 120, and reset.
  • Opposite-phase behavior of the two lights.
  • Closest-car selection for each avenue and lane.
  • Closest-light selection in both coordinate systems.
  • Acceleration when no obstacle exists.
  • Stop and reverse thresholds.
  • Velocity smoothing with known inputs.
  • Periodic boundary wrapping.
  • Deterministic initialization with a fixed random seed.

API Tests

  • Valid simulation creation.
  • Rejection of negative, non-integer, or excessive vehicle counts.
  • Isolation between two UUID instances.
  • Correct advancement by exactly one step.
  • Unknown and expired identifiers.
  • Simulation deletion and lifecycle cleanup.

Frontend Tests

  • Setup request payloads.
  • Button state transitions.
  • Polling start and cleanup.
  • Zero-car analytics.
  • Sprite selection by traffic-light state.
  • Coordinate transformations by avenue.
  • Plotly series calculations.
  • Error messages when the API is unavailable.

Possible Improvements

Future versions could include:

  • Julia Project.toml and Manifest.toml files for reproducible installation.
  • Environment-based API configuration.
  • Schema validation for all request parameters.
  • Explicit POST /simulations/:id/steps semantics for state changes.
  • Simulation metadata, deletion, expiration, and persistence.
  • A server-owned simulation clock or WebSocket state stream.
  • Protection against overlapping client requests.
  • Seed selection for repeatable experiments.
  • Non-overlapping vehicle placement.
  • Correct empty-avenue analytics.
  • Normalized time values returned by the backend.
  • Live Plotly updates instead of post-run rendering only.
  • Configurable green, yellow, and red durations.
  • Realistic car-following models such as IDM.
  • Calibrated acceleration, braking, reaction time, and vehicle length.
  • Turning movements, lane changes, pedestrian phases, and collision detection.
  • Queue length, waiting time, throughput, density, and travel-time metrics.
  • Responsive SVG scaling and improved accessibility.
  • Unit, integration, API, and end-to-end tests.
  • Continuous integration for Julia and JavaScript.

Project Status

The repository contains a complete academic prototype with:

  • A runnable Agents.jl traffic model.
  • Coordinated traffic-light agents.
  • Rule-based vehicle behavior.
  • A Genie API for isolated simulations.
  • A React/Vite visualization client.
  • Interactive scenario controls.
  • Post-run Plotly speed analytics.
  • Public source code under the MIT License.

No automated test suite, Julia environment lock, or production deployment configuration was identified in the reviewed repository.


References

The project was conceptually inspired by:

Wilensky, U. (1998). NetLogo Traffic Intersection model. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.

View the NetLogo Traffic Intersection model


License

This project is licensed under the MIT License.

Copyright (c) 2026 Rodrigo López Guerra.

The software is provided without warranty, subject to the terms included in the repository license.

About

Interactive full-stack traffic simulator built with Julia, Agents.jl, Genie, React, and Plotly. It models vehicle behavior and coordinated traffic lights using agent-based rules, real-time SVG visualization, and velocity analytics.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages