Building an Airport Simulator in Nine Weeks
1 September 20265 min readAlfieRjavateamworkfrontendmodelling

Dorset Software wanted a tool that models how an airport handles traffic. You set up a configuration, changing the flow rate of aircraft, the arrangement of runways and various other parameters, then run it and see how the airport copes once disruption is introduced.
There were six of us and nine weeks, five of which were for development. We ended up with Airport Traffic Studio, a JavaFX desktop application that met every requirement in the specification, including all the optional ones, along with a few things nobody had asked for. I worked mostly on the frontend, including a 3D visualisation of the airport.
Big Early Decisions
We settled on MVVM early. The simulation engine runs on its own thread and mutates state every tick, so instead of letting the interface read from it directly, the ViewModel produces an immutable snapshot of the world state on each tick and the view renders against that.
The reason for it was thread safety, and it worked. What I hadn't expected was how much it would do for us as a team. The snapshot became the contract between the frontend and backend, which meant the engine and the interface could be built in parallel as long as we agreed on what a snapshot contained. That turned out to be worth more than the concurrency guarantees were.
The other early call was scrapping a Python component for the statistical modelling before any of it was written. It would have meant inter-process communication with the Java engine, latency during real-time simulation, and asking users to install two runtimes, none of which was justified when the standard library already did what we needed.
Both of these were made in the first fortnight, and both kept paying off for the rest of the project. That's the thing I'd most want to remember from it. Architectural decisions compound: a good one keeps returning value every week afterwards, in ways you often can't predict when you make it, and the places where we hadn't thought as carefully were exactly the places that were awkward to change later. Time spent early is much cheaper than time spent later.
The 3D visualisation
The specification suggested a 2D representation. We built it in 3D instead, with camera controls, rendered runways and taxiways, and animated aircraft circling in the holding pattern, queuing on the taxiway, taking off and landing.
I hadn't used JavaFX before, let alone the 3D side of it, so most of this was worked out as I went along.
The interesting problem was decoupling the animation from the simulation. My first attempt moved aircraft once per simulated tick, which stuttered at low tick speeds and performed badly at high ones. The fix was to stop tying rendering to ticks at all, and instead interpolate each aircraft's position from how much real time had passed since the last tick, at a constant 60fps. Changing the simulation speed then has no effect on the render.
That solved smoothness but not cost, since rebuilding the scene every frame gets expensive with a lot of aircraft. So the scene only rebuilds when the environment actually changes, and between rebuilds only the existing nodes are moved. The per-frame cost then stays flat no matter how busy the airport gets, which is what let it hold up under stress testing at 4000 flights an hour.
The visualisation earned its place in ways I hadn't anticipated. Having a working frontend early in each sprint gave the backend team something to validate against, and rendering their changes in 3D repeatedly surfaced bugs that would have been much harder to spot from unit tests, simply because you could see an aircraft doing something obviously wrong.
It also won us the award for best user interface out of every team on the project, which I'll happily take given that none of us had touched JavaFX nine weeks earlier.
![]() |
|---|
| View of the Simulation page, including the interactive 3D representation |
Working as a team
We ran Scrum across five one-week sprints and all five finished on schedule with a working increment, which I think is the strongest thing I can say about how we organised ourselves. Version control was disciplined throughout, with feature branches and pull requests reviewed before merging, and that held up right to the end.
The part that helped most wasn't in the methodology at all. When someone finished early they'd start on the next sprint's work or go and help another sub-team, which kept people busy and meant everyone ended up understanding more of the codebase than just their own section. It was entirely informal, and a shared backlog of lower priority tasks would have made it deliberate rather than accidental.
Communication was good enough that problems generally got caught early, though more contact between the frontend and backend sub-teams would have prevented some of the integration friction later on. There were points where we weren't aligned on what data the snapshots would contain, and that caused late changes an earlier conversation would have avoided.
The other thing we'd have done with more time is user acceptance testing. We finished with 336 passing tests and were confident the software worked, but all our evidence that the interface is actually usable came from the six people who built it. It's an easy thing to let slide when the deadline is close.
What I took from it
This was the first time I'd written Java in a team rather than on my own. Working within MVVM made that unusually clean, since the snapshot boundary meant I could build the entire frontend against a well-defined interface without waiting for the engine underneath it.
Most of what I learned was about that rather than about the language. Getting six people building one thing at once relies less on individual competence than on the structures you agree on early, and the value of getting those right is that they keep paying you back long after you've forgotten you made the decision.
