Let's address the elephant in the room, the 800-pound gorilla next to it, and the bull in a china shop crammed in there by the door: the video below is Morrowind running on my iPhone with modern touch controls at a flawless 60 FPS.
Right below that is another classic, Duke Nukem 3D, also running on an iPhone, fully leveraging the 120 Hz ProMotion display. And for our third example we have Serious Sam: The First Encounter...
I managed to get polished, fully functional source ports of Duke Nukem 3D and Serious Sam running on iOS in a little under a week, burning through most of the weekly usage on a $100 ChatGPT Pro account. Morrowind took considerably longer: several weeks of trial and error, dead ends, and ultimately rethinking how I was working with the AI altogether.
The strange part is that none of this is really my field. I'm a gamer and a tech journalist. I don't have the slightest idea how to code.
There are so many ways AI is changing our lives right now, but I don't think any of them have brought me quite as much childlike joy as getting some of my favorite PC games up and running on my iPhone by myself, in a matter of days, with no training or human technical assistance, simply because I decided they should (and I wanted to see if I could...).
Morrowind on an iPhone is amazing by itself. But the fact that anyone can just DO THAT is nothing short of mind-boggling. Not by requesting a feature from a developer, waiting for a publisher, or hoping an abandoned port gets revived, but by taking an existing open-source project and pushing it toward the particular thing they want.
These truly are strange times.
From Pocket PC Ports to AI Coding Agents
I've always had something of a weak spot for janky ports on otherwise unsupported hardware. When I was a kid, I didn't have a PSP. I had a PlayStation One in the basement, despite it already being 2004, and a rather sad PC. My dad did, however, give me his old HP iPAQ after deciding he had no particular use for a Windows Mobile Pocket PC.
When you're stuck with a PS1 for much of your childhood, you get creative with the resources you have on hand. I had a LOT of fun figuring out how to get just about everything under the sun running on that Pocket PC.
For some inexplicable reason, the original Age of Empires had received an official Windows Mobile port, complete with the single-player campaigns and touchscreen support. It played surprisingly well on a 3.5-inch display.
There were plenty of unofficial homebrew ports floating around the internet, too. I played the original Tomb Raider with its tank controls mapped onto the iPAQ's tiny directional pad, along with Quake and even Quake II. The latter ran at a blistering eight frames per second, which was technically a slideshow but felt miraculous at the time.
The point was never that these were the best versions of those games. They plainly weren't. But I managed to get them running somewhere they had no obvious business running at all, and I got hours and hours of fun in the bargain.
When I eventually got an Android device, one of the first things I did was look for PC and console games I could force onto it. I spent years tinkering with half-finished emulators, abandoned source ports, and awkward, shoehorned control schemes in builds no one had maintained in years.
I followed projects like the Android ports of Doom 3 and Jedi Academy, along with early builds of OpenMW. I admired what their developers had accomplished, but there was often too much jank involved. Those games never quite became something I would simply pick up and play.
I'm not a developer by any measure. I'm allergic to code. Heck, I struggled with Hello World in the computer lab. Still, I became fascinated by the culture surrounding these projects: the idea that if something you wanted didn't exist, you could inspect the source code, gather a few other stubborn people, and try to make it yourself.
With projects I've followed for years, like OpenMW (open source implementation of The Elder Scrolls III: Morrowind) and Daggerfall Unity, I've often thought that IF I had the skill set to contribute or make improvements, I definitely would. That was always the end of the conversation, at least until the past few months and the meteoric rise of AI coding agents.
This is where vibecoding comes into the picture.
No, seriously. I was very skeptical myself, and I'm not a fan of poorly produced "AI slop." I didn't believe typing a vague request into a chatbot would magically deliver functioning software. And surprise, surprise, that's not how it works.
What caught my attention was reading about a developer who had used AI agents to port Command & Conquer: Generals to iOS, complete with touch controls designed around the platform. Google's AI Studio Lead Ammaar Reshi posted about the accomplishment on X and said he'd leveraged a "human+AI" workflow together with Fable, where Anthropic's flagship model did the grunt work while he handled on-device playtesting and quality control.
– Ammaar Reshi (@ammaar) July 4, 2026
This was a project a single developer managed to implement in just a few days. In Ammaar's own words, much of the human involvement consisted of directing Fable and its sub-agents, testing their output, identifying problems, and steering the system back toward the intended result. That didn't sound like pressing a magic button marked "make game" and waiting for Fable to spit out native arm64 code. It sounded more like managing a fast, capable, yet blinkered technical team.
I run a digital marketing agency as my day job and manage a team spread across Eastern Europe and Asia. Directing a capable but disjointed team of collaborators toward clearly delineated goals IS my special skill set.
And that gave me a slightly deranged thought...
What If I Managed AI Agents Like a Real Team?
What if I treated AI coding agents the same way I treat my team working on a marketing project, except directed toward building iOS ports of classic PC games?
I honestly didn't expect it to work. Regardless, I grabbed a ChatGPT Pro subscription and started thinking about the simplest credible test case.
Duke Nukem Was the Proof of Concept
That brought me to EDuke32, the open-source implementation of the Build engine used by Duke Nukem 3D. The Build engine is relatively simple by modern standards. EDuke32 already ran on Apple Silicon Macs, which meant at least part of the journey onto Apple hardware had been completed. More importantly, someone had begun work on an iOS port roughly 12 years earlier before apparently abandoning it.
I wasn't asking the AI to drag an alien codebase onto a completely unsupported platform. There were existing Apple-specific foundations, traces of an earlier mobile effort, and a clear target. If anything out there was going to be a relatively straightforward vibecoded porting job, this was it.
I was already $100 out of pocket. I figured the worst possible outcome was that I would burn through a few evenings, exhaust my usage limit, and emerge with nothing except hallucinations and compiler errors I couldn't even understand. That assessment proved only partly correct.
The first objective was modest enough: get EDuke32 compiled as a native iOS app, sideload it onto an actual iPhone via AltStore, then launch it and reach the menu without the whole thing collapsing.
I wasn't asking for gyro aiming, customizable touch controls, a game launcher, or anything close to a polished mobile port yet. The Build engine first had to, forgive the pun, build. Then it had to open. Then it had to locate the game files, render something recognizable, and accept input without immediately crashing.
This is where I started figuring out what vibecoding actually looks like in practice.
I didn't simply type "please port Duke Nukem 3D to iOS" into ChatGPT and return two hours later to find an IPA file gift-wrapped and ready to sideload. The process was far more involved, and honestly far more interesting, than that.
First, I set up a public repository for eDukeiOS. Then I opened two separate ChatGPT instances. One was a ChatGPT 5.6 Sol Pro chat that took on the role of project director. It would strategize, analyze completed work, and issue project orders. I drew heavily on my professional experience here because this is essentially what I do for digital marketing clients. I put myself in the client's shoes and defined a properly scoped project requirement.
The second instance was Codex on PC, running 5.6 Sol Codex at high inference. This was the implementation agent, the one that would actually modify the codebase and get the work done.
I started with what was effectively a client onboarding discussion with the "project director" chat. I explained the general requirement: I wanted to get Duke Nukem 3D, and potentially other Build engine games, running properly on my iPhone and iPad. I also provided relevant information, including the public EDuke32 repository, and asked it to analyze the state of the open-source project and assess whether the idea was even feasible.
That's about when we hit the first roadblock: I was asking this AI coding setup to generate an iPhone app, but I didn't own a Mac to run Xcode and didn't have a paid Apple developer account.
One thing that stood out about the "Fable-class" models arriving around this time, including Fable 5 and GPT 5.6 Sol, was how much better they seemed at big-picture reasoning and coming up with novel solutions. GPT 5.6 demonstrated this by becoming the first model to win a game in ARC-AGI 3. Essentially, this class of models appeared better at thinking beyond the most obvious path through a problem (and arguably more human-like).
When I told the project director I didn't have a Mac, it reasoned that this wasn't necessarily a dealbreaker. We could spin up a GitHub macOS runner, essentially a constrained Mac VM in the cloud, and compile the code there. GitHub provides runner access for public projects.
Problem solved.
Next came reviewing the existing EDuke32 source code. We were in luck. Someone had worked on an early iOS port roughly 12 years earlier, and the remnants were still sitting in the repository. That code couldn't run on modern devices as-is, but it gave the project director a foundation to build on.
The agent came back with a feasibility report covering what could realistically be accomplished and the steps required to get there, from simply compiling and launching the code to adding optimized touch controls and eventually porting advanced renderers like Polymost, which would allow newer Build-engine titles such as Ion Fury to work.
We were in luck: someone 12 years ago had worked on an early porting effort and it was still there in the repo. That code couldn't run on modern devices as-is but it gave the project director a foundation to build on. It came back with a feasibility report - what could be accomplished and how long it'd take, from just getting the code to compile and load to adding in optimized touch controls, to porting over advanced renderers like Polymost that supported newer Build titles like Ion Fury.
I had it scope out the entire project milestone by milestone. Acting as the client, I reviewed the plan and greenlit it. That's when the fun part started.
I essentially sat between the director chat and the implementation Codex instance running on my PC. The director produced detailed work orders that defined each milestone in unambiguous language and required a completion report at the end. I would shuttle those over to Codex, which was easier than it sounds thanks to PC remote access through the ChatGPT mobile app.
Instead of giving the agent vague instructions and letting it wander off in whatever direction seemed appropriate, it was limited to specific, delineated tasks. It had to document the work completed, any errors or blockers it encountered, and then report back. I'd hand those reports to the director chat.
My role wasn't to understand every line of code it produced. I couldn't. My role was to define what success looked like, break the work into small enough pieces, test the results, identify when something improved or regressed, and stop the agents from drifting away from the objective.
That made sense to me. When managing a marketing project, I don't personally write every article, design every landing page, or even speak every language involved. I define the outcome, assign work based on the available capabilities, review what comes back, identify problems, and make sure the team's overall effort remains directed toward a specific goal.
The AI workflow obviously wasn't identical. But the general process of directing specialized work toward one clearly defined outcome carried over surprisingly well.
The director would use Pro-level inference to assess the reports and completed work, validate changes made to the GitHub repository, and then issue another work order. Sometimes that meant moving on to the next milestone. Other times it meant correcting or repairing whatever had just gone wrong.
With this production loop in place, we actually produced our first compiled .ipa file in under an hour, ready to sideload and test.
A beautiful EDuke32 icon appeared on my iPhone's home screen...
I tapped it. and it immediately crashed.
That was, well, kinda deflating. Fortunately, the app had produced a log file in its documents folder. I shared that log with the project director chat. It analyzed the output, identified potential causes, and wrote a fresh work order for Codex.
We iterated on that loop for the next few hours. There was always another crash, another log file, another work order.
Sometimes the game would reach a black screen for a few seconds before crashing. Sometimes a 16:9 rectangle would flash onscreen for a moment, a relic of that ancient unfinished port. Sometimes nothing happened at all.
The director chat would explain why it thought something had broken. Much of the explanation was jargon I couldn't wrap my head around, but I could still nudge it toward certain hypotheses based on my limited contextual understanding and what I was seeing on the device.
We kept at it. And then, suddenly, success.
The yellow radiation-hazard logo appeared, followed by the 3D Realms splash screen.
Duke Nukem 3D running natively on an iPhone through eDukeiOS.
We made it to the menu! And then... again, nothing. Touch controls hadn't been implemented yet, so there was no way to interact with the game. There was no audio either.
But still, there it was: the actual Duke3D menu screen and a game that had no business running at all on my iPhone.
The Human Part Mattered More Than I Expected
We locked into the grind, made more progress, and this is where human-driven interaction started to matter a lot more than I expected.
I play a lot on mobile, probably as much as I do on PC. I'm a sucker for smooth gyro and touch aiming, and proper button remapping.
I don't play it that often, but Call of Duty: Mobile has to be the gold standard for touchscreen FPS controls. The icons are resizable, readable, and mostly in the right places by default. Gyro aiming makes competitive play viable, and high refresh rates elevate the whole experience. Prince of Persia: The Lost Crown and Assassin's Creed Mirage also had surprisingly well-implemented touch controls.
On the flip side, there are games with truly god-awful touch controls that make you wonder whether the developers ever bothered playing their own titles on mobile for more than 10 minutes.
I was excited when Rebellion's Sniper Elite 4 landed on iOS. I became considerably less excited five minutes into playing it, when I realized swipe aiming depended on a literal virtual right stick. Aim and keep your finger down onscreen, and your view starts to drift, as if someone had bolted a DualSense joystick to the side of the display.
Kentucky Route Zero has pinch-to-zoom controls that inexplicably reset the moment you take your fingers off the screen. If you want to enjoy close-ups during conversations, you basically have to keep your fingers in claw mode while playing.
Death Stranding and Capcom's Resident Evil ports are probably the most egregious offenders of all. These games have "touch controls" only in the loosest legally compliant definition of the term.
They slap an entire virtual DualShock controller, complete with buttons and joysticks, over the screen and call it a day. You can't even tap menu icons directly. You use the "left joystick." Aiming is a joke because it means wiggling your finger over a tiny fixed virtual stick that drifts like a real joystick instead of simply swiping to move the camera.
A lot of those older Android port projects, like the OpenMW port or JK Touch, the old Jedi Academy port, also had lackluster touch input. Buttons were ugly or placed in strange locations, and configuration was often limited. You could forgive an indie porting effort for not having the best-designed touch controls. But Capcom selling Resident Evil games for $40 on iOS with a DualShock overlay slapped on top is just offensive.
I quickly learned that vague feedback produces vague solutions.
I figured this was where I, as a non-coder but avid gamer, could add the most value to the project beyond shuffling chat reports and logs around. I quickly learned that vague feedback produces vague solutions. "The controls feel bad" isn't useful feedback, and the next iteration would often come back the same or worse. What did work was having a clear vision, properly scoped and clearly communicated.
I came up with a responsive touch-control concept for eDukeiOS that provided full functionality and easy aiming without filling half the screen with icons. It was inspired by the CoD: Mobile overlay, but with a few twists of its own.
The screen is divided into a movement zone and an aiming zone. The left side reads input as a virtual joystick, but the control itself is invisible, so it doesn't take up screen real estate. The right side provides standard swipe aiming with configurable sensitivity.
There is no fire button by default. Instead, you can tap anywhere in the right zone to fire, or sustain fire by holding your finger down for 120ms or longer. Gyro aiming is enabled by default, with toggles and configurable sensitivity. The result is a surprisingly smooth control scheme that leaves far more of the screen unobstructed than I expected.
I started with a prompt that went more or less like this:
"The left side of the screen should function as a movement zone. The right side should control the camera continuously. A quick tap should fire, but a swipe should never be mistaken for a tap. The buttons should be movable, resizable, and transparent enough that they don't cover half the game. Assess feasibility and let me know what you think, let's discuss the core goals behind the interface after."
This essentially let the project director chat grill me and understand not just what I wanted from the interface, but why – that established guardrails to ensure the controls lived up to my vision for them.
The touch interface itself took only two or three turns of our iterative development loop to implement. It was probably the easiest part of the entire process, yet it added an enormous amount of value in terms of actually making the game enjoyable on a phone.
The entire porting process was an eye-opener to me, but the ease of adding a custom touch interface really stood out. If I could implement customizable and genuinely enjoyable touch and gyro controls in about 30 minutes with Codex, what on earth are multiplat studios like Capcom, with hundreds of actual developers, thinking when they ship mobile games with cheap controller overlays bolted on?
If I could implement genuinely enjoyable touch and gyro controls in about 30 minutes with Codex, what on earth are multiplat studios like Capcom, with hundreds of actual developers, thinking when they ship mobile games with cheap controller overlays bolted on?
Bug fixes and minor feature additions took another two nights of work and a good chunk of my weekly usage limit (I never had to pay beyond GPT's $100 Pro plan). But at the end of it, I had eDukeiOS: a functional native port of EDuke32 running Duke Nukem 3D smoothly in its software renderer, with touch controls that felt good enough that I could simply pick up the phone and play.
It JUST WORKED.
Then I copied over the data files from Ion Fury, which runs on a heavily modified version of the same engine. We went through another few development rounds to implement the Polymost OpenGL renderer that Ion Fury uses. That worked, too.
At that point, the project started feeling less like a technical novelty and more like something that could stand toe-to-toe with premium PC and console ports on the App Store.
This would have been a perfectly reasonable place to stop. Instead, I immediately started wondering whether I could do the same thing with a proper polygonal 3D game.
Serious Sam: The First Encounter now runs just as well. I'm testing a build of Selaco and making progress on porting id Tech 4, which would open the door to Doom 3, Quake 4, and last year's indie hit Skin Deep.
I burned through the entire weekly usage limit on Pro, fortuitously got a usage reset, and burned through all of that a second time. That gave me time to work on this article with those ports sitting on hold in GitHub until the weekend arrives and my usage resets again.
Not too bad for someone who's barely coded a single program in his life, eh?
Then Morrowind Broke My Entire Workflow
By then, I thought I had the workflow figured out. Duke Nukem and Serious Sam had shown me what coding agents could do when the path forward was relatively straightforward. What I wasn't ready for was Morrowind.
Morrowind ended up becoming the biggest win by far, but it was also a completely different kind of project. People have talked about getting OpenMW onto iOS for more than a decade and, if the forums are anything to go by, made several failed attempts along the way. I eventually managed it, but compared with Duke Nukem and Serious Sam, this was on another level entirely.
I initially expected the same straightforward process. Codex cheerfully cloned OpenMW, did some... things, tried compiling an .ipa, and promptly fell flat on its face. OpenMW, the open-source reimplementation of Morrowind's engine, is a completely different beast. It relies on dozens of dependencies for audio, rendering, physics, input, and more.
After several days using my old methodology, we had barely moved. The orchestrator kept developing tunnel vision. Faced with a class of rendering errors, it would fix them one by one.
The most hilarious, punch-the-screen example came when it took "produce a working .ipa" far too literally and got stuck repeatedly compiling an installable file instead of, well, fixing the actual problems. I could ask, "Look, are you playing whack-a-mole with the errors and hyperfixating again?" It would agree, issue a broader work order, and then do exactly the same thing again.
It was solving the immediate problem, translating a desktop OpenGL shader, for example, without returning to the real one: getting OpenMW to run.
That's when it hit me.
If I simply told the system to fix problems, it would happily play shader-compatibility whack-a-mole until the heat death of the universe. I needed it to propose hypotheses about what COULD work, test them, and then act on the results.
Not: "Fix this error."
More like: "If we do X, will it move the project state forward by Y?"
Work orders started getting bounded end states: success; failure with a known cause; failure that revealed the next diagnostic test; or failure that ruled out one path and moved us to the next likely hypothesis. That changed everything.
Even a black screen or failed build could now represent progress, as long as it told us something useful about what to try next.
We kept at it and, suddenly, reached the Morrowind loading screen!
Followed by an immediate hard crash, of course.
By then, both chats were nearly out of context. They were stuffed with work orders, logs, and enormous walls of text. As a stopgap, I had the director set up a shared Google Docs ledger, but that eventually ballooned past 100,000 words, roughly the length of a novel.
The better solution was to turn each work order into a numbered .md file in the GitHub repo, so the chats could pull in as much (or as little) history as they needed.
ControlPlane: Giving AI Projects Memory, Goals, and Structure
Between the hypothesis-driven loop and durable external memory, this had effectively become a software layer sitting on top of the agents, organizing and coordinating their work. I've named it ControlPlane, and I'm building it as an MCP server on GitHub.
ControlPlane lets the project remember, reason, and coordinate outside the AI chat itself.
A context window is a bit like working memory: enormous, but finite. Run a project long enough and it fills with failed experiments, logs, decisions, and half-forgotten assumptions. Replace the chat and its successor has to reconstruct what happened and, more importantly, why.
ControlPlane moves that state into a durable, versioned structure, separating the project brief, current state, work orders, decisions, and evidence. The chat no longer has to serve as the project database. That sounds laughably simple. It is. Writing things down has been a pretty successful human technology.
"Writing things down has been a pretty successful human technology."
But writing everything down would only create a meticulously documented whack-a-mole process. The other half is the bounded reasoning loop:
current state → hypothesis → work order → evidence → review → updated state
Rather than asking the worker to "fix the renderer," the orchestrator starts with the evidence, asks what we still don't understand, forms a testable explanation, and issues a bounded work order.
The hoped-for result can fail while the experiment itself succeeds by ruling something out or revealing missing evidence. ControlPlane remembers what we know. The work order tests what we don't. The worker produces evidence. And the orchestrator decides what that evidence allows us to believe next.
The division is simple:
- The human owns the purpose
- The orchestrator owns planning and project state
- The worker owns execution and evidence
Workers report what happened. The orchestrator decides what it means. That might sound bureaucratic until you run an AI engineering project long enough to watch an enthusiastic worker promote its own assumptions into project truth. Instead of starting a fresh worker with 40,000 words of project history, the worker can simply be told:
"Read the current state. Read the active work order. Retrieve only the decisions and evidence that work order references. Verify the live repository state. Execute."
With that process in place, we kept at it. After a few more weeks of Codex wrangling, it just worked.
Morrowind now runs on iOS with rock-solid performance and touch-native controls, and it's probably one of the best gaming experiences I've had on the platform.
I wouldn't claim that ControlPlane alone is what "made Morrowind work." The engineering agents still had to solve an unreasonable number of actual technical problems, and we haven't run the kind of controlled comparison that would justify making that causal claim.
That may actually be the more interesting lesson. As AI becomes more capable of technical work, the limiting factor may increasingly be less about whether a model can write code and more about whether the system around it can remember what matters, decide what to test, learn from failure, and stay pointed at one objective long enough to finish.
That sounds much grander than what I originally set out to do.
For me, this was still wish fulfillment of the highest degree: I wanted a game to run on my phone, so I made it happen. The kid in me with a hand-me-down HP iPAQ is practically doing backflips with joy.
This project isn't a demonstration of technical skill or savvy. I think it proves something much more interesting and valuable: we might genuinely be entering a new era of DIY coding and development, with all the good AND bad that entails.
Vibecoding Has a Slop Problem
Yes, vibecoding and the broader use of AI in software development have been facing increased backlash, and for valid reasons.
The team behind RPCS3, the popular PlayStation 3 emulator, recently hit out against "AI slop" commits on its GitHub project. The developers said they've been overwhelmed by users submitting AI-generated code they don't necessarily understand themselves, which can break in unexpected ways and make debugging even harder than it otherwise would be.
That criticism is worth taking seriously. Non-coders producing and sharing AI-generated code fundamentally don't know what that code does at a functional level. When something breaks, they're often dependent on AI all over again to understand and repair it. And because AI doesn't necessarily approach problems the way an experienced developer would, it can introduce flaws or bizarre architectural decisions that make life harder for human collaborators.
The biggest open-source projects have millions of users, and some have enormous enterprise and government installed bases (if we talk about certain Linux distros for instance). They've built immense amounts of trust through decades of accountable, high-quality work.
That accountability is key. There are clear checks and balances in place to prevent abuse and maintain a more-or-less consistent user experience. You can download 7-Zip and be reasonably confident that it will, indeed, extract your .zip and .7z files and won't randomly delete the contents of your C drive.
That doesn't mean we should write off AI programming or vibecoders. Quite the opposite.
Maybe Software Is Becoming Personal Again
My experience tells me vibecoding has the potential to democratize software and our access to it. At a time when corporations are increasingly turning games we buy into limited licenses to download, that matters.
It also taps into some of the purest ideas behind open-source software: treating software as a form of self-expression and as a tool for everyday problem-solving.
I wasn't around when people made mixtapes. I was born in the mid-90s, so I think I missed that era by a couple of years. But I understand the principle. I think vibecoded ports and other personalized software mods could function a lot like mixtapes once did. There's no expectation of professional quality or consistency, or really anything beyond having fun, expressing yourself, and turning something that already exists into something a little more uniquely your own.
I see myself less as an "AI developer" and more as the modern-day equivalent of some guy burning an Arctic Monkeys mix CD. Many of today's biggest open-source projects began with one person writing code to solve a problem that annoyed them, or because they thought they could build something better than the tools already available, and then deciding to share it. Institutionalization, along with all the responsibility that comes with it, arrived later.
The question, then, is how you actually go about doing this. And that's fairly straightforward. You don't necessarily need to be a programmer, although being tech-savvy and having some awareness of fundamental software and hardware concepts helps enormously.
Most of us aren't cut out to be coders. But AI coding tools can absolutely help enthusiast gamers build, personalize, experiment, and self-express through software. At the end of the day, you don't have to be a musician to appreciate music. Or to make a mixtape.


























