Project – Race simulator

Some information

Every so often I like to race in my simulator. And, from time to time, I race together with some friends. We organize a weekend where we train on Friday and race a 24-hour race from Saturday until Sunday. One of us will be racing for 2 hours and then, during a pit-stop, we swap places. The game is streamed live to another monitor or TV and the others usually sit in another room watching the other race.

While racing you want to be able to focus on the task at hand: be consistent, be quick and keep it on the track. That’s where the others come in. While one is racing, the others can keep a look out for various things. Oncoming trafic, backmarkers, tires that are worn out, fuel level, etc. While racing, you are able to see all this information, but the information is usually tucked away inside several menu’s. There is also a whole plethora of information that is simply not accessible trough the GUI.

The “problem”

We used to ask the current driver to pull up the information, but this causes unwanted distraction. Not only that, all three of the “benched” drivers wanted this information. The other issue was that if we asked the information from the driver, it would almost instantly be obsolete. Tire wear could look good, but one lockup could change the whole scenario. We needed a solution.

Challenges

Maybe the biggest challenge would be that we use not one, but multiple different simulation software. We decided to focus on two for now: IRacing and ProjectCars2. We wanted to be able to view the data as real-time as possible on any device we saw fit. This meant that we needed to build something that was able to handle multiple “sources” as data input and be able to display it into something that would be viewable on multiple different devices.

The DataProvider

Because we were focusing on two different applications, we first needed to check how we would be getting the data.

IRacing

IRacing has a really big community behind it and this was working towards our advantage. One of the community members created a SDK for IRacing(irsdk). This was a Python based solution we could build on to fetch information directly from IRacing.

Project Cars 2

PC2 was a bit more labor intensive. It does not directly expose any sort of API, but rather it will store all it’s current race data in mapped memory. This was something we could read and interpret.

From file

It’s a bit difficult to develop while sitting behind a simulator, so we created a tool that could do two things:

  • It could read the data real-time and store it to a file (for both IRacing and PC2).
  • It could write the data to Mapped memory the same way PC2 did.

This enabled us to do two things:

  • We could write previous data back to MMAP to imitate a running PC2 session.
  • Because we could define the structure of the file we write to, we can also create a connector that can read this file. Because of this, both IRacing and PC2 data would be stored the same way. This would eliminate the need to implement some sort of IRacing imitator.

Connectors

To be able to handle different sources, we created connectors for each possible data-source. These connectors all have the same “Get” method and would return all possible data in a simple dictionary, with the first key being the connector type (IR, PC)

Providing the data

To create a generic solution, we opted to create a python solution that would do the following:

  • It would startup a WebSocked listener where other applications can subscribe to to get real-time updates
  • Then it would check what simulator is running and startup an update loop for the desired connector.
  • Every “tick” it would call the “get” function from the loaded connector and return the raw data (dictionary) provided.

The Middleman

We created a NodeJS server that would be doing three things

  • Subscribe trough a websocked to the DataProvider
  • When it receives an update with data, map it from the simulator to a generalized data structure
  • Setup it’s own websocked where clients can subscribe to and send any updates to the clients.

The main reason we opted to do the mapping in the Middleman is to remove unwanted processing from the DataProvider. It’s sole role should be fetching and providing the data. This also allowed us to change any incorrect mapping without needing to redeploy the DataProvider.

Furthermore, in a later stage, we would be able to split up the data into specific parts. Depending on the page the client currently is, we would most of the time only need a subset of data. The client would then subscribe to a specific “page update” (for example a page with a tire overview) and we would only need to send the data the client actually needs. When the client would change page (for example the map), it would unsubscribe from it’s current page and subscribe to the new page.

The Client

As the client we created an Angular application. This would subscribe to the middleman and watch for any updates that were send it’s way, depending on it’s current page. This meant that implementing any new page was just a matter of checking what data we need for the current page, make it available inside the Middleman and subscribing to it.

Last thoughts

This project was from before when I started this website, thus I don’t have any screenshots on hand. Nonetheless, this was easily one of the more fun projects that I’ve done. For one, it involved something I really like to do when not working (simracing) and I embarked on this together with a friend. We spend several evenings researching the ways we would be able to get the data, deciding on the data structure and the infrastructure of the entire application stack.

This project also created several side projects (like writing MMAP to file and re-writing the file to a MMAP) and allowed me to learn a lot about several things like threading in Pyhing, MMAP and pub-sub principles.

10/10 that I would do this again.

Copyright © 2026 LuMa Dynamic. All Rights Reserved.

About me

With more than 15 years of .NET experience, I have built a solid technical foundation as a developer. Over the past couple of years, I have also developed myself as a Scrum Master, a role in which I can fully apply my passion for collaboration, coaching, and continuous improvement. I gain energy from guiding teams toward greater self-organization and effectiveness, but also from learning new and exciting technologies.

Contact

Information
l.mateijsen@lumadynamic.com
+31 6 24 87 87 15
LinkedIn

Hours
Mo-Fr: 09:00–17:00