Saltar al contenido

Card Video Game Made with Local AI: How I Built It on Two Modest Graphics Cards

The War of the Threshold: a fan of six illustrated cards from the four factions (a masked queen, an armored warrior, a crystal mage, two antlered figures and a warrior queen) under the game title.
The War of the Threshold: a complete card video game with 212 illustrations, its own music and a full translation, built with local AI on two mid-range graphics cards.

Dear friends,

I have a lot of things pending to show you about local AI workflows for humble hardware, but I felt like taking a break and using what we have been developing in the previous articles just to have fun playing.

And by playing I don’t only mean playing with my local AI workflows, but literally playing.

So I ventured into creating a video game.

Because showing you the tools, putting them to the test and showing you what can be achieved with old hardware and without paying for a single token, so that you can see how far it is possible to go, is my mission.

And this is just another example. But besides entertaining you by reading about or reproducing the laboratory experiments, this time you will be able to enjoy trying out a fun card game.

A complete card video game made with local AI: what came out of it

It is called The War of the Threshold and it is a two-player card game with a campaign, online matches and a free mode. It is not a mock-up or a technical demo: you can play it from start to finish.

  • 212 cards split across four factions, each with its own illustration, flavor text and effect.
  • 340 generated images with Qwen-Image on an RTX 2080 Super to end up with those 212 final illustrations, because many had to be redone several times.
  • 39 music tracks composed with ACE-Step on an RTX 2060 and 32 sound effects generated by code.
  • Four campaigns of eight encounters each, online matches with level-based matchmaking, friends, private rooms and a free mode.
  • About 10,000 lines of Python for the game, another 3,500 for tests and 260 automated tests.
  • Interface, cards, campaign and guide fully in Spanish and English.

All on one PC with an 8 GB RTX 2080 Super, a 6 GB RTX 2060 and 32 GB of RAM.

In this article I explain how I built it, step by step: the team of agents that did it, how they were organized so they would not leave the plan, how the illustrations were painted, how the music was composed, what went wrong and how it was fixed. If you are interested in local AI workflows on modest hardware, this is the most useful part.

How to play The War of the Threshold

If you have played round-based card games, the system will sound familiar. If not, it is simpler than it looks.

Main menu of The War of the Threshold over a foggy valley with a huge moon: in the distance a red citadel, a blue crystal spire, a violet tower and a giant tree; the buttons on the left and the player card on the top right.
The main menu: campaign, free mode, online match, friends, collection, decks, game guide and credits.

There is no health or mana. Each card adds power points to one of three rows (melee, ranged or siege) and whoever scores more wins the round. You win the match by taking two out of three rounds. Each player starts with two rubies and loses one every time they lose a round.

A match of The War of the Threshold: three rows per side with unit cards, the player's hand at the bottom, the scoreboard on the right with the opponent in red and the player in blue, and the floating sheet of the Field Healer card while hovering over it.
The match: three rows per side, your hand at the bottom and the scoreboard on the right, with the opponent in red and you in blue. Hover over a card to see its full sheet: effect, row, power, rarity, rule and flavor text.

The fun is in the effects. Some cards get stronger when they are together, others double a row, others destroy the strongest cards on the field, weather reduces everything to one point, and spies are played on the opponent’s field in exchange for drawing cards. Each effect has its own round badge, so you recognize it at a glance.

There are four factions, each with its own style and passive: the Realms of Ash draw a card when they win a round, the Arcane Circle keeps a unit on the field, the Court of Umbra wins ties and the Wild Clans always start the match (unless the opponent is also Wild).

Card swap window before the match: the player's hand shown large, card by card, with arrows, the card details and the thumbnail strip.
Before starting, a window shows your hand large, card by card, so you can swap up to two.
A golden coin spins in the middle of the board under the title "Who goes first?" to decide who plays first.
If nobody has the advantage, a coin decides who starts.
Campaign screen: the four factions on top, the list of eight encounters of the Realms of Ash campaign and the encounter details with the opponent and the reward.
The campaign: four stories, one per faction. Every victory gives you new cards.

The team of local AI agents: who did what

What I really wanted to test was not just the game. It was whether a small studio of local AI agents can be coordinated to build it, each with its own specialty, without any of them stepping on the others.

The design is mine. All the development work was done by the agents of Agente Jiménez, my panel of local agents. If you don’t know what an AI agent is exactly or what is behind it, I explained it in detail in that same article.

Who did what diagram: Acuantico Power designs; Adolfo and Adolfo-Assistant, with the Cyber Tiel and Gemma 4 models, plan; Adolfo-Dev programs with Cyber Tiel, Adolfo-Picasso paints with Qwen-Image on the RTX 2080 Super, Adolfo-BPM composes with ACE-Step on the RTX 2060 and Gemma 4 translates; everything goes through quality gates.
Who did what: I design and review; the planners split the work; each agent does its part on its own GPU; and everything goes through quality gates.
AgentLocal modelWhat it didGPU
Adolfo and Adolfo-AssistantCyber Tiel and Gemma 4Planners: split the game into small tasks and check every resultBoth (the model is split across them)
Adolfo-DevCyber Tiel, with OpenCodeProgramming the game and its testsBoth
Adolfo-PicassoQwen-Image 2.1 in ComfyUIThe card illustrations and the backgroundsRTX 2080 Super
Adolfo-BPMACE-StepThe musicRTX 2060
AdolfoGemma 4The full translation into EnglishBoth

Cyber Tiel is a 35-billion-parameter programming model of the “mixture of experts” type, with only 3 billion active per token. I use the Cyber-Tiel-Coder-35B-A3B APEX-I NanoPlus version, which takes about 13 GB. Gemma 4 is actually the gemma-4-26b-a4b QAT version, another mixture of experts (26 billion parameters, 4 billion active) of about 15.6 GB. Neither fits entirely in an 8 GB card, but with both GPUs sharing the model, and the spare experts in system memory, they perform well. Adolfo-Dev programs with OpenCode, a terminal programming agent, backed by Cyber Tiel. I serve the models with LM Studio; if you want to set it up yourself, there is the LM Studio installation guide and how to use it as a server from Python.

ModelRTX 2080 Super aloneWith both cards
Cyber Tiel (35B, 3B active), measured with the Q4_K_S version13 to 14 tokens per second19.7
Gemma 4 26B (4B active)16.626.9 to 30

I measured the Cyber Tiel figures with its Q4_K_S quantization, about 22 GB; the smaller version I use now has not been measured separately. These are not cloud speeds. But for an agent working at night while you sleep, they are plenty. And if you wonder how far these models can go programming on their own on a machine like this, I measured it in Autonomous coding with local AI: is 8 GB of VRAM enough to run 30B models? and I compared thirteen models in What Is the Best Local AI Model?.

How the local agents work: small tasks, tests first and protected files

This is what I learned the most. A local model with a context of 20,000 or 30,000 words cannot “understand” a whole project and, if you ask it to, it leaves the plan.

It happened to me. My first attempt ended up discarded because the agent did not respect the design document: it made up generic data, repeated sentences and skipped rules that were written down. It was not a lack of ability. It was a lack of framework.

The second time I turned the problem around. Instead of asking it for a game, I gave it very strict rules and very small tasks.

The design document and the tasks

The first thing was to write the complete design: the game rules, the four factions, the 212 cards with their rarity, power, category and a description of what the image shows. That design lives in text files the agent cannot touch.

With the design closed, the work was split into numbered tasks, each in its own file, with the test it must pass:

  • The narrative texts of each faction: the flavor text of its 53 cards.
  • The card catalog builder, which joins the design and the texts into a single data file.
  • The artificial intelligence of the opponents.
  • The player database: accounts, collection, decks and campaign.
  • The campaign generator, reproducible: a test regenerates it and checks that the result is identical.

The agent’s rules

Every conversation with the agent starts by reading a very short rules file. The most important ones:

  • One task per conversation.
  • No files of more than 150 lines per write: if it does not fit, split it into modules.
  • If the same error appears three times in a row, stop and report it, do not improvise.
  • Forbidden to touch the tests, the design and the check scripts, and forbidden to “lower the bar” of a test to make it pass.
  • Forbidden to generate game data with randomness or templates: the texts had to be real, with no repetitions or typos.

Protected by fingerprint

Rules alone are not enough: a local model in a hurry “fixes” the test instead of the code. That is why the acceptance tests, the design and the check scripts are protected by fingerprint. A script stores the hash of each protected file and makes them read-only. If the agent modifies them, the check detects it and the task fails.

After each delivery, another script runs the tests and returns a short, clear result. It matters that it is short: every word it takes up in the agent’s context is a word it can no longer use to think.

The orchestrator and the watchdog

So that it could work without me, I wrote an orchestrator. It launches the tasks one after another, each in a new session, approves reasonable permissions, rejects dangerous commands, retries with the real error if something fails, cancels sessions that have been 30 minutes without activity and stops if the agent touches a protected file. When it finishes it leaves a report.

A second process, the watchdog, checks that everything is still alive and, at the end, runs the full tests and leaves a verdict. The longest tasks, the opponent AI and the database, took between 25 and 30 minutes each. I only read the report when it was done: all correct and 186 tests green.

This method has a lot in common with what I explain about agents in the Agente Jiménez article: a local model does not need more intelligence, it needs an environment that prevents it from making known mistakes.

The illustrations with Qwen-Image and ComfyUI: 340 images for 212 cards

Every card needed its own illustration, and all 212 had to look like they belonged to the same game. It sounds simple until you try it.

The truth is that 340 images were generated to keep 212. One out of every three had to be discarded and redone, some more than once, because the model did not understand what it was asked or because, simply, it came out wrong.

And it was not done in a single night. It took several nights of generation and many spare moments during the day to fix badly planned designs, redo cards and write and adjust all the code.

A recipe by code, not a prompt per card

To get consistency I did not write 212 descriptions by hand. Each image is built by code from a recipe with a common style, its faction palette, its category (character, creature, machine, spell, relic) and a short description of the subject. Each faction has its own atmosphere: the Realms of Ash, a red sunset sky among smoke and embers; the Arcane Circle, a night sky; the Court of Umbra, violet fog and moon; the Wild Clans, an old, damp, dark forest.

The model is Qwen-Image 2.1, with 7 billion parameters and int8 quantization, which I run with ComfyUI. I installed it with Pinokio, as I explained in the guide to installing Flux with Pinokio. From Agente Jiménez, the Adolfo-Picasso agent starts and stops ComfyUI, sends it the recipes and stores every result in a gallery.

Adolfo-Picasso interface in Agente Jiménez: the ComfyUI and RTX 2080 Super status, the prompt box and the gallery with the card illustrations and backgrounds already generated.
Adolfo-Picasso in Agente Jiménez: from here it starts ComfyUI, receives the recipe for each card and stores every result in the gallery.

Before launching the 212, I painted only four representative cards to validate the style. For each image I fixed one seed per card, so any illustration can be reproduced exactly.

What it costs to paint on an 8 GB card

On the RTX 2080 Super, each 768×1152 pixel image takes about two minutes. The 340 are almost twelve hours of GPU time. Three things had to be taken care of:

  • Before generating, the LM Studio language model is unloaded from memory to leave the graphics card free.
  • If less than 8 GB of RAM is free, the agent refuses to start ComfyUI. I learned the hard way that, with tight memory, the whole machine freezes.
  • Every ten images, a five-minute rest so the card, which also drives my desktop, can breathe.
Diagram of the generation nights with two graphics cards: the RTX 2080 Super generates 340 images (for 212 cards) at about 125 seconds each and the RTX 2060 composes 39 music tracks at about four or five minutes each.
During the generation nights, the 2080 painted the illustrations while the 2060 composed the music.

What the model cannot draw

What took the most work was not generating. It was finding out what the model can actually draw.

  • Crossbows, catapults and racks came out crooked again and again, with two bows or with pieces that did not fit. I replaced those objects with bows, battering rams, trebuchets and guillotines, which it does understand.
  • It could not draw the sling in any way. It became a javelin.
  • A pot of burning pitch ended up stuck to the soldier’s face; a torch in a raised hand, on the other hand, it draws without any problem.
  • My first wolves were normal wolves with odd teeth. I had to redefine the Wild Clans as metahumans with traits of forest animals, and since then an automatic check warns if a description does not comply.
  • For one of the Wild heroines, with bird traits, the model gave her the body of a hyena with a human head; I had to describe the beak, the crest and the bird eyes separately.
  • The Realms of Ash filled up with banners and gears because of the palette. I banned them in the recipe.
  • If the subject carried a second weapon, the model drew it twice. Now the recipe only mentions the weapons the card really carries.
  • The Court of Umbra must always wear a mask and no green, because the word “green” crept into the background.

So before generating anything, a script checks each description against its faction’s rules. Generating a bad image costs two minutes of GPU; checking it beforehand costs milliseconds.

Human review, card by card

The images were not approved by the model. I reviewed them myself, one by one, in a web page I built for that, with a “correct” checkbox, a “regenerate” checkbox and a note about what is wrong.

Image review web page with five Realms of Ash cards: three marked as correct, one unreviewed and the Tower Crossbowman marked to regenerate with the note "the crossbow comes out crooked and with two bows".
The review web page I built for this: each card with its description, its “Correct” box, its “Regenerate” box, the note about what is wrong and, below, the improved prompt before regenerating.

That note becomes an improved description and the card is regenerated. Over time I refined the process: instead of regenerating many at once, a single candidate per card, which I see in a separate tab and approve or reject with a note. And whatever is discarded is also deleted from the gallery, so as not to accumulate images that are no longer needed.

The result, faction by faction:

Mosaic with the illustrations of all the Realms of Ash cards: soldiers, archers, siege machines and heroes under a red sunset sky.
Realms of Ash: disciplined armies under a red sunset sky, smoke and embers.
Mosaic with the illustrations of all the Arcane Circle cards: mages, constructs and light creatures under blue night skies.
Arcane Circle: mages, constructs and constellations under a night sky.
Mosaic with the illustrations of all the Court of Umbra cards: masked figures, spies and shadows in violet fog.
Court of Umbra: intrigue, masks, violet fog and moon.
Mosaic with the illustrations of all the Wild Clans cards: metahumans with animal traits in an old, damp, dark forest.
Wild Clans: metahumans with animal traits in the old, damp, dark forest.

I also painted the screen backgrounds with the same workflow: the menu hall, the collection study table, the board’s war table and, last, the main menu background, for which I generated four different scenes and chose the four fortresses one.

The music with ACE-Step on the RTX 2060 and the sound effects by code

The 6 GB RTX 2060 has no room for Qwen-Image, but it is plenty for ACE-Step, a model that composes music from a description.

So while the 2080 painted cards, Adolfo-BPM composed on the 2060: 39 tracks of 150 seconds each, at about four or five minutes per track. Each one comes from a different description, such as “lively tavern jig” or “cheerful courtly minuet”, and they are recorded in a manifest so that any of them can be regenerated without losing the style.

Every context of the game has its own music: the menu, the library, the campaign, each faction’s map, the duels, the bosses, and the victory, defeat and draw endings. The audio manager shuffles each list and does not repeat a track until all have been played, and it picks the music according to the chapter you are in.

The tracks were not approved by the model either: I built a tool to listen to them one by one, keep the ones that work and discard the ones that don’t.

The sound effects, 32 in total, are not generated by any AI: a code script synthesizes them. A click, a horn or a sword hit does not need a model of billions of parameters, and the result is identical every time. By default, the music and effects volumes are set low on purpose, because in the first match a game that shouts scares more than it hooks.

The game engine, the server and the client

Under all of this there is a real game, with an architecture designed so it can be tested and so that nobody cheats.

  • Engine: deterministic, with a seed. The same match with the same seed always gives the same result, which lets you replay and check any match.
  • Server: FastAPI with WebSocket. The server is in charge: the client only says “I want to play this card” and the engine decides whether it is legal. The client never declares a winner.
  • Database: SQLite, with accounts (passwords with scrypt), collection, decks, campaign and friends.
  • Client: Pygame, with a medieval and magical style, animations and music.
  • Online matches: level-based matchmaking with a margin that widens as you wait, friends list, invitations, private rooms and 120 seconds to come back if your connection drops (if you do not, you lose the match and your opponent wins).

I tested it with 260 automated tests. The server ones I also validated with “mutations”: the code is deliberately broken in 17 ways and the tests are checked to detect it. They caught 16, and the one that slipped through turned out to be equivalent.

Collection screen: grid of Arcane Circle cards with their power, effect and row badges and the number of copies.
The collection: all 212 cards, with their illustration, effect and row badges, and how many copies you own.
Deck editor: the deck list on the left, the faction cards in the middle with their copy counter and the selected card details on the right.
The deck editor: 25 cards from a single faction, with a copy limit per card.

The opponent AI: too good, and how I leveled it by measuring matches

The AI that plays against you decides by simulating every possible play on a copy of the match and keeping the one that gives the most points. There are three difficulty levels: apprentice, strategist and master.

It works. Too well.

When I tested it, the opponent in the first encounter of the campaign almost always won and scored nearly double my points. For a tutorial, a disaster.

So I stopped giving opinions and measured. I had an intermediate-level automatic player with the basic deck play dozens of matches against each opponent, and I tried two remedies:

  • That the opponent’s deck uses only cards the player could already have at that point of the campaign, and that it adds up to at most a fraction of the player’s strength.
  • That the AI fails on purpose, choosing a random play with a probability that decreases as the campaign advances.
First encounter of the campaignPlayer wins before levelingPlayer wins after
Realms of Ash27 %85 %
Arcane Circle43 %65 %
Court of Umbra40 %70 %
Wild Clans47 %82 %

Difficulty rises throughout the campaign until the bosses are a challenge again. And when you beat the four campaigns, a free mode opens, with random-faction opponents and an Elo-type rating that goes up and down with your results.

Icons, animations and guide: details that make the game understandable

A card game with effects is hard to read if all cards look alike. Almost half of the final polish was making it understandable at a glance.

  • Round badges for every effect (bond, morale, horn, spy, medic, muster, scorch, decoy and the three weather types) and another one for the row where each unit plays.
  • A floating sheet with the full description when you hover over any card.
  • A clear scoreboard, with the opponent in red and you in blue, big points, rubies and a box that says whose turn it is.
  • Animations for everything that happens: cards fly to their row, the opponent “thinks” for a moment before playing, a coin decides who starts and every round ends with a notice.
  • A reward envelope that flies in when you win an encounter, and opens to show you the new cards.
Victory screen with the reward's new cards laid out, the NEW label under each one and the sheet of the hovered card.
Every campaign victory sends you an envelope with new cards. Hover over one to read it.
  • An integrated guide with a first-match tutorial, the rules and every badge explained.
Game guide, abilities section: each round badge (bond, morale, horn, spy, medic, muster) with its explanation.
The game guide: each round badge with its explanation.

The translation into English with Gemma 4

This is an ideal task for a local model, and I gave it to Gemma 4: the names and flavor texts of the 212 cards, 280 interface phrases plus 80 patterns with numbers and names, the campaign texts and the game guide.

So as not to leave anything in Spanish by mistake I built a tool that goes through every screen of the game in English and lists any phrase that is still untranslated. Until it returns zero, it is not finished. You can switch language instantly from Settings.

Deployment, online server and version control

The online server runs on a VPS with a system service and a reverse proxy with encryption, with database backups. I deployed it with an end-to-end test from the outside: register two players, pair them and play a match.

The game also checks its version. Every time it starts, and while the server is active, it asks which is the latest version; if yours is older, it warns you with a link to this page. The server can also reject a client that is too old when something changes that makes it incompatible.

Credits screen: Thanks for playing, created by Acuantico Power, a note that this is a laboratory experiment and a button to support on Ko-fi.
The credits: this is a laboratory experiment, with a link to this article.

What I learned building a video game with local AI

  • Local models don’t need a project: they need a task. Small, with a test and with a framework that keeps them from straying.
  • Protect what must not be touched. Tests and design out of the agent’s reach prevent it from “fixing” a test instead of the code.
  • Check before generating. An automatic rule that rejects a bad description saves hours of graphics card time.
  • Plan to repeat. To keep 212 good images, plan to generate 340. It is not a failure: it is the cost of having judgment.
  • Measure, don’t opine. The opponent seemed “a bit hard” to me; the simulation said it won almost every match.
  • Split the cards. One painting while the other composes multiplies what comes out of each night of work.
  • And review with your own eyes. The AI does the work. The judgment is still yours.

If you want to keep following this line, you have more experiments in my Local AI and artificial intelligence systems section and the tutorials.

Download, versions and online matches

Experiment version (1.0.0): with its bugs

These are the files exactly as they came out of the experiment, and they stay here as its result, with the errors that showed up when testing them. Download The War of the Threshold 1.0.0 if you want to see the experiment as it was (a single file, nothing to install). On Linux, extract the archive and double-click LaGuerraDelUmbral; on Windows, extract the zip and open LaGuerraDelUmbral.exe. You will have to accept the license the first time. Since the program is not digitally signed, Windows may warn “Windows protected your PC”: click “More info” and then “Run anyway”.

The most important bug in 1.0.0: it does not connect to the official online server, so neither online matches nor friends mode work. And since it never reaches the server, it cannot tell you that a newer version exists either: if you already have it, download the current one by hand from below. The yellow frame below marks what is kept as the result of the experiment.

EXPERIMENT RESULT · 1.0.0 with its bugs

Current version (1.0.1): just for fun

These buttons offer the most recent release, but just for the pleasure of it: it is no longer the result of the experiment and does not count as such. It fixes bugs and improves the experience for users (for example, it connects properly to the official server for online and friends matches), so it is the right download for anyone who just wants to enjoy the game. You install and open it the same way as the previous one. If all you want is to play, this is the best choice: the one in the green frame.

THE BEST CHOICE IF YOU JUST WANT TO PLAY · 1.0.1

This page is the home of the game: the latest version will always be published here, and right now it is 1.0.1.

The game takes care of reminding you (from 1.0.1 onwards). Every time you open it, and while my server is active, it checks whether you have the latest version. If you don’t, it shows a notice with a direct link to this page to download it, and if an old version stops being compatible, it tells you and asks you to update. The link in the notice goes to the page in your language: the Spanish or the English one.

The online server is official, but temporary

Online matches (level-based matchmaking, friends and private rooms) are played through my server. While it is open, that is the official server.

But it will not last forever: I will keep it active only temporarily.

When I close it, I will publish here the code and instructions so that you can create your own local server on your computer and play PvP matches with your friends.

The campaign and the free mode against the AI are played on your own computer and do not depend on my server.

If you like what you see, if you want this to keep growing or simply want more laboratories like this one, you can buy me a coffee.

Keep exploring local AI

Frequently asked questions about making a video game with local AI

Can you make a complete video game with local AI?

Yes. This one has 212 illustrated cards, music, a campaign, online matches and an English translation. What you need is not a huge machine but a method: small tasks, tests, protected files and human review.

What hardware do you need?

I used an 8 GB RTX 2080 Super, a 6 GB RTX 2060 and 32 GB of RAM. It can also be done with a single card, but it takes longer because you cannot paint and compose at the same time.

How many images do you have to generate for 212 cards?

Many more than it seems: I generated 340 to keep 212, because one out of every three had to be redone. At about two minutes per image on an RTX 2080 Super, that is almost twelve hours of GPU time.

Which image model did you use?

Qwen-Image 2.1 (7 billion parameters, int8 quantized) in ComfyUI. The most important thing was not the model but knowing what it can and cannot draw: crossbows, slings and catapults came out wrong and I swapped them for objects it does understand.

How do you compose music with local AI?

With ACE-Step, a local model that composes from a description. Each 150-second track took four or five minutes on an RTX 2060, and I generated the sound effects by code.

Why do local models get lost in long projects?

Because their context is short, about 20,000 to 30,000 words. The solution is to give them small tasks, a very clear framework of rules and tests they cannot touch.

Can you play online?

Yes, while my official server is active: matches with level-based matchmaking, friends list and private rooms. It is a temporary server; when I close it I will publish on this same page the code to set up your own local server and play PvP with your friends.

How do I know if I have the latest version?

The game checks by itself when it opens, as long as the official server is active. If there is a newer version, it warns you with a link to this page, which always offers the latest one.