Building a Fully Automated Weekly Racing Challenge Generator
What if a racing game didn't need a developer to manually prepare its next challenge? Instead of creating a track, selecting an environment, composing a soundtrack, exporting assets, uploading files, and publishing a new build every week, the entire process could run as an automated content pipeline

What if a racing game didn't need a developer to manually prepare its next challenge? Instead of creating a track, selecting an environment, composing a soundtrack, exporting assets, uploading files, and publishing a new build every week, the entire process could run as an automated content pipeline. That was the idea behind Weekly Race Generator: a system that creates and publishes a new playable racing challenge on a recurring schedule. The important distinction is that the racing track itself is procedurally generated, while the surrounding creative assets—skybox, music, artwork, and loading screen—are generated using AI. The result is a pipeline that combines deterministic procedural generation with generative AI and cloud infrastructure. A weekly racing challenge consists of several pieces: A procedurally generated racing track An AI-generated skybox/environment AI-generated background music AI-generated promotional artwork A loading screen derived from the generated environment Metadata describing the competition All of the assets required by the game The goal was to make these pieces come together automatically. Once the system is running, the intended workflow looks roughly like this: The interesting part isn't any individual generator. It is connecting all of them into a reliable pipeline. The system treats each weekly challenge as a competition with an identifier such as: s1-w22 The current competition is stored in a current.json object. This became important because the generator should not depend on an environment variable to determine which week comes next. Instead, the pipeline: Reads the current competition. Determines the next competition. Generates the new challenge. Publishes it. Updates the current competition only after successful generation. This makes the competition state part of the generated content rather than deployment configuration. It also means that invoking the generator again naturally advances the competition. The track is deliberately not AI-generated. The geometry is generated algorithmically according to the rules of the racing game. This provides something that generative image models aren't particularly good at guaranteeing: a track that is actually usable by the game. A procedural generator can enforce constraints such as: Track continuity Closed-loop topology Start/finish placement Appropriate curvature Track length Driving direction Valid checkpoints Game-specific gameplay requirements The output is therefore deterministic in structure even though the resulting layout can vary from one competition to another. This separation of responsibilities is useful: Algorithms guarantee gameplay; generative AI provides atmosphere and creative variation. Once the track theme is established, the system generates the visual environment. The skybox is produced using a generative image model. For example, a futuristic city theme might produce an environment containing elevated highways, towers, neon lighting and atmospheric effects. The generated skybox then becomes an input to another stage of the pipeline. This was an important design decision. Initially, the loading screen and skybox were generated independently. Even when both were generated from the same textual theme, they could look like two completely different worlds. The solution was to make the generated skybox the visual source of truth. Theme │ ▼ Skybox AI │ ▼ Generated Skybox │ ├──────────────► Game environment │ ▼ Image-to-image generation │ ▼ Loading Screen The loading screen is therefore treated as a cinematic crop of the same generated world rather than a separate interpretation of the theme. This produces much stronger visual consistency. The visual assets aren't the only generative component. Each competition also gets its own music. The music generation stage uses Google's generative music capabilities through the Genblaze integration. The generator produces a soundtrack based on the theme and desired mood of the competition. This means that a weekly challenge can have its own combination of: Track layout + environment + soundtrack + artwork without requiring a human to manually assemble those assets every week. The Python application uses the Genblaze SDK as the foundation for interacting with the generative media providers. Rather than writing the entire application around one particular provider implementation, the project keeps generation behind application-level interfaces. Conceptually: Competition Builder │ ├── Track Generator ├── Skybox Generator ├── Music Generator ├── Artwork Generator └── Loading Screen Generator │ ▼ Genblaze / Providers This separation makes the pipeline easier to evolve. For example, changing how a particular asset is generated does not need to change the competition orchestration itself. The application is responsible for deciding what needs to be generated. The provider integrations are responsible for deciding how it is generated. Once generation is complete, the resulting assets need somewhere to live. That's where Backblaze B2 Cloud Storage comes in. The generated challenge is packaged with its assets and uploaded to B2. The storage hierarchy effectively becomes the content distribution layer for the game. A simplified representation looks like: B2 └── competitions/ ├── s1-w21/ │ ├── current.json │ ├── manifest.json │ ├── skybox.png │ ├── loading.png │ ├── artwork.png │ ├── music.wav │ └── track... │ └── s1-w22/ └── ... The important architectural decision here is that B2 becomes the source of truth for published content. The generator doesn't need to remain available after a challenge has been published. The game only needs to retrieve the published challenge. One surprisingly important part of the system was temporary file management. Some generation operations naturally produce files locally before they can be uploaded. That works differently in a serverless environment. AWS Lambda provides a writable temporary filesystem through /tmp, while the deployed application itself is effectively read-only. The application therefore uses a dedicated temporary directory during generation: /tmp/scr-weekly-assets/ The lifecycle is: Generate │ ▼ Write temporary asset │ ▼ Upload to B2 │ ▼ Delete local asset Cleanup is part of the successful generation lifecycle rather than an optional housekeeping operation. After a successful run, the temporary generation directory should contain no leftover assets. This became particularly important before moving the application into Lambda because a locally working application can accidentally depend on filesystem behavior that doesn't exist in a serverless environment. The generator is packaged as a Python AWS Lambda function. The Lambda entry point is intentionally thin: Lambda handler │ ▼ build_default_app() │ ▼ CompetitionApp.run() │ ▼ CompetitionBuilder The generation logic does not live inside the Lambda handler. This keeps the application executable in two ways: Local CLI ───────┐ ├──► CompetitionApp Lambda handler ─┘ The same application code can therefore be tested locally and executed by Lambda. This was useful during development because I could run the generator locally while developing the pipeline, then deploy essentially the same execution path to AWS. One of the more interesting problems appeared only after deploying the application. The initial Lambda invocation failed with: No module named 'pydantic_core._pydantic_core' The Python package was present, but one of its native components had been built for the wrong platform. This is an easy problem to encounter when building Lambda deployment packages on macOS. My local machine was: macOS Python 3.11 while Lambda runs on AWS's Linux environment. Python dependencies containing native extensions therefore cannot always simply be copied from the local virtual environment. The deployment package needs dependencies built for the environment Lambda actually uses. This is one of those issues that isn't obvious from the application's Python code itself. Everything can work perfectly locally and still fail immediately in Lambda because the binary dependencies target a different operating system or architecture. With the generator working as a Lambda function, the final piece is scheduling. AWS EventBridge Scheduler invokes the Lambda periodically. The resulting architecture is: EventBridge │ scheduled trigger │ ▼ AWS Lambda │ ▼ Weekly Race Generator │ ┌─────────┴─────────┐ ▼ ▼ Generative AI Procedural providers systems │ │ └─────────┬─────────┘ ▼ B2 Storage │ ▼ Game The generator no longer needs a human to start it. A scheduled event starts the process, the application determines the next competition, generates the content, publishes it to B2, and updates the competition state. That is what turns a collection of generators into an autonomous content pipeline. Generating an image or a piece of music is relatively straightforward. The harder problem is making the entire operation reliable. A weekly generation job needs to answer questions such as: What happens if an AI request fails? What happens if an asset uploads successfully but cleanup fails? What happens if the Lambda invocation is interrupted? When should the competition number advance? What happens if the same job is invoked twice? Where is the published version of the challenge? How can I determine what happened after the job finishes? These questions shaped the architecture more than the individual generation APIs did. The application has explicit configuration validation, structured logging, temporary-file cleanup and a clear publishing lifecycle. AWS CloudWatch provides the execution logs, making it possible to inspect a generation run after the fact. Putting everything together: EventBridge │ │ schedule ▼ AWS Lambda │ ▼ Read current.json │ ▼ Determine next week │ ┌─────────────┼─────────────┐ │ │ │ ▼ ▼ ▼ Procedural Skybox AI Music AI Track │ │ │ │ ▼ │ Loading Screen │ Image-to-Image │ │ └──────┬──────┴──────┐ ▼ ▼ Artwork Challenge │ Metadata └──────┬──────┘ ▼ Upload to B2 │ ▼ Publish challenge │ ▼ Update current.json │ ▼ Cleanup /tmp The game doesn't need to know how any of this happened. It simply consumes the latest published challenge. The most interesting part of this project wasn't generating the individual assets. It was discovering how different technologies complement each other. Procedural generation is good at producing structured, rule-constrained content. Generative AI is good at producing creative, visual and musical content. Genblaze provides an abstraction layer for connecting the application to generative providers. AWS Lambda provides an execution environment without needing a continuously running server. EventBridge provides the recurring trigger. Backblaze B2 provides persistent storage for the generated content. Together, they form something that is more interesting than any one component: A system that can continuously create new game content without requiring a developer to manually assemble and publish every update. The natural extension is to make the generation pipeline increasingly autonomous. For example, future versions could use gameplay telemetry to influence generation: Player telemetry │ ▼ Difficulty analysis │ ▼ Generation parameters │ ▼ New procedural track │ ├──► New environment ├──► New music └──► New artwork │ ▼ Next challenge Instead of simply generating a new challenge every week, the system could learn what makes a challenge interesting and adapt future challenges accordingly. That would turn a scheduled content generator into a feedback-driven content system. Weekly Race Generator started with a relatively simple question: Could a racing game continuously produce new content without someone manually building each week's challenge? The answer is yes—but the interesting engineering challenge is not simply calling generative AI APIs. It is building the infrastructure around them. By combining procedural game-content generation, generative media, Genblaze, AWS Lambda, EventBridge and Backblaze B2, it is possible to build a pipeline where the creation and publication of new game content becomes an automated operation rather than a recurring manual task. The broader idea extends well beyond racing games. The same architecture could be used for games that need continuously changing environments, levels, quests, artwork, music or other forms of generated content. The game becomes the consumer of a content pipeline that can keep creating new experiences on its own.
Key Takeaways
- •What if a racing game didn't need a developer to manually prepare its next challenge? Instead of creating a track, selecting an environment, composing a soundtrack, exporting assets, uploading files, and publishing a new build every week, the entire process could run as an automated content pipeline
- •This story was reported by Dev.to, covering developments in the dev space.
- •AI advancements continue to reshape industries — read the full article on Dev.to for complete coverage.
📖 Continue reading the full article:
Read Full Article on Dev.to →


