DEV Community

Ibrahim Kashif
Ibrahim Kashif

Posted on Originally published at Medium

How Campfire Displays An Interactive Game To The Users Browser

Campfire is an Agentic System that builds entire games with simple prompts. You can think of it as a tool for the mind. A way to enhance an individuals creativity and find a way to turn that into a reliable source of income and enjoyment for them.

The perspective that Campfire is not made for "Developers" but rather "Everyday people" really matters and it's been the d*efining principle for the architecture of experience* surrounding Campfire.

We've tried our best to make it as easy as possible for the user to genuinely create. Understanding what they need to know and make decisions on. And not telling them anything about what genuinely does not concern them or what Campfire can handle on its own. 

This is the perspective of the best Agentic Systems.

Picture Of Playing A 100% Campfire Generated Game.

However experience cannot bottleneck actual technology. And for Campfire to actually create games and iterate on a game and display it to the user we have to cheat a little bit to ensure we keep the beautiful elegance of Campfire's hidden architecture. 

This article is meant to help you immerse yourself in the months of iteration to finally conclude the architecture of Display and Compilation as we have derived today. Please take time to really understand the flow, as if it were a river so that you may understand the peace and serenity of development and iteration and the excellence of flow.

The Problem:
A "Game Engine" is a collection of foundational tools and rules needed to build a video game. Every single game you've ever played has been built on a game engine. Something like "Unity, Unreal Engine etc".

These not only hold tools for a human to create **within the Game Engine software but also the fundamental scripts to **run logic like physics and lighting.

Campfire is an Agentic System that handles the "human creation" part within a game engine. We use the software's tools to do your part of manual labor. However we are still entirely reliant on the mercy of the game engine's "actual engine logic" to run whatever Campfire has built as the user has specified.

For this very reason one can conclude that for Campfire to work, we need the Game Engine's backbone, regardless.

Now imagine what this means, every single game **that's being created by Campfire needs a "process" of the game engine **open and running. And to open the game engine to run the "artifacts" Campfire has created we need a computer to run the game engine.

So does one have to assume that you need a computer running a game engine open for every single game someone wants to create using Campfire? Imagine 500 users creating a game in a day. Does that mean we need to rent 500 computers?

And how do we display the "running game" to the user's browser? The user is considered a 'non technical', he has no idea about "game engine". He doesn't want to see the overly complex UI of it either. So how could we possibly display something running on a computer to a user's browser. Without them waiting 10 minutes for a single compile or us having to rent 500 computers for 500 users.


Picture of the UI of a popular game engine software (Unity)

Here are the methods we implemented to solve exactly this problem and the limitations of each:
Preface: So we looked at this problem and we concluded that we should break this down into 2 separate problems:

  • Compiling the game
  • Displaying the compiled game

All the following methods that we invented and pioneered revolve around us trying to solve these 2 problems. Each method had its pros and cons. And the reason why we scrapped it.

**Method 1: WebGL Compilation on the users own computer

Displaying WebGL to the users computer:**
So at first before Campfire was even in a production environment we ignored the question about "How are we going to serve this to x amount of users" and instead focussed on the singular question of "How do we even get anything shown to the user?". We wanted very clearly to not show the "Game Engine" processes in front of the user because it would confuse them and destroy their creativity.

But the **only place **the game is actually viewable from is the engine's own "UI". 

That was, until we discovered WebGL.


Picture of WebGL loading a Campfire Generated Game From A Singular URL

WebGL is a coding tool that lets websites show 2D and 3D graphics right inside your** web browser** without needing extra plugins. For the end users it's a simple "type this URL inside your browser and you can play the game". The most simplest of concepts. We thought it was perfect.

At the time we were using NGROK which was a way for our backend to talk to our frontend. We wired the frontend iframe to fetch the URL from the backend which would give it a local file path /files/ and that would be uploaded and downloaded on the frontend.

We had no issues with HTTPS or TLS certificates because the NGROK URL handled much of our problems. However unknown to us we were paying a major cost to take this through NGROK.

Imagine the flow: WebGL is produced on your Computer as a local folder (/g14/changes/c1). Then our browser gets the URL and fetches for it /files/g14/changes/c1. Our computer uploads the files over the internet to NGROKS servers and then the browser downloads it over the internet from NGROKS servers. Even though it's a local /file/ on the user's computer and so is the browser. 

What did this result in? Well regardless of the actual compile time to produce a WebGL build inside the Game Engine. Just to download it to the browser took around 3–4 minutes and was entirely dependent on the mercy of your bandwidth which at times could be so slow that you'd be sitting there for **10 minutes just waiting **for the browser to download the "change" folder.

What was the size of our file? It was 50mb ish. Nothing. However because of this routing problem we had this issue and it was unknown at the time.

Compiling WebGl via the Game Engine:
Our major concern at the beginning however was not related to the browser download speed because 3 minutes was considered alright at the time. What really concerned us was the raw "compile time" for our game engine to turn our game into WebGL. It was 300 seconds.

Now for comparison Campfire created for you an entire game in roughly 400 seconds. Now imagine having an entire game created, all the code, all the assets, the ui, everything and then you'd have to wait 5 minutes just for it to compile and 3 minutes for it to display.

If that was bad, one of the core principles of Campfire is 9*0% good first output and 100% iteration tools. **We are absolutely with the concept of iteration as any good engineering project requires iteration. Imagine if you just did a singular script change or you resized or moved the character. A tiny change, you'd have to wait **8 minutes* to view that change. This killed the entire idea and at the time the only concept for viewing your game was "WebGL" so we knew we had to optimize the compile step.

Optimizing WebGL Compilation via the Game Engine:

This was our original baseline breakdown. Eventually we did a bunch of optimization steps to get the compile time down to:

**Asset-only changes: ~7s
Script changes: ~51s, breakdown:

  • Domain reload + script detection: ~2s
  • Scene re-authoring (549 FactoryCli calls): ~3s
  • ProducePlayerScriptAssemblies: ~1s
  • Compressed player packaging: ~4s
  • Postprocess (IL2CPP/Emscripten/wasm-ld/wasm-opt): ~39s, of which wasm-ld link alone ≈ 27s**

This was really good. However combine this with the bottleneck of WebGL - browser we'd still be waiting +5 minutes per iteration which was absurd and we did not want that for our users.

So we started figuring out new ways.

**Method 2: Renting Computers And Render Streaming To Users Browser

Render Streaming:**
We later heard of a new process of being able to display the compiled game to the frontend called "Render Streaming". Render streaming is a process much like twitch or any stream that you watch live. A technology that lets you run a heavy, high-quality 3D application on a powerful server or computer, and stream the live video and audio directly to a web browser on a smartphone, tablet, or low-powered laptop.


Picture Of Render Streaming (Using Sample Scene).

For this process we could ignore the entire WebGL pipeline which as you saw from the last method was the real cost (mainly in the displaying to the browser) but also in the compile breakdown: "Postprocess (IL2CPP/Emscripten/wasm-ld/wasm-opt): ~39s, of which wasm-ld link alone ≈ 27s"

We went ahead and tested it and were not impressed. This invited Latency + Resolution Bitrate Issues. But the major factor was that our users wouldn't have to wait (60 second compile time + 3–4 for webGL browser download) 5 minutes per iteration. It could be instant paying only the "compile time" which we knew was extremely short after removing WebGL entirely.

We went ahead and tested the latency and it was completely fine locally. The resolution was prone to fluctuations depending on your internet (obviously) but even at the best connection it couldn't stream much sharp edges and text was muffled. We thought it was a worthwhile compromise to ensure time, especially if we introduced a "Produce Build" manual button on Campfire for when users wanted to publish their build to s*how their friends before uploading it to steam in 2 clicks.*

Iteration would be fast and reliable.

Renting Out Computers:
Now Campfire had been in development and testing for around 7–8 months and we were ready to move to the production environment. We rented a server to store Campfire's backend and got to the question of "How are we going to use a Game Engine when the user doesn't have one on their computer?".

Our idea was to rent GPU computers which had everything installed on it along with a launch script that launched the Game Engine and the related processes. Our backend would send files to it (Artifacts) via upload and download and it would stream P2P with the user (Browser to Computer directly).

Now we can't just keep GPU computers on indefinitely so we devised a solution called "Worker/Seat + Warm Pool Instances". The idea was a demand based orchestrator that launched computers based on flux of the userbase, kept a warm pool filled for quick start and reused ones that have gone idle before terminating them if no users needed to "claim" them.

We managed to optimise to get 2 users on a computer **instead of a singular computer for one user and could scale this to **3 or even 4.

Now besides the obvious fact that we now required to rent computers, this did introduce a latency issue **for the Render Streaming. Though render streaming was working **P2P directly from the users browser to the computer and back. This was obviously entirely dependent on where the user was and where the computer was.

Compared to the local host the latency went from** 1–5ms to 120–150ms** (if you were in the same continent) or over the pacific 300+.
Besides that the time to launch a worker (based on cold boot or warm boot) were roughly:

**Warm Boot:

  • Boot - 200s
  • Launch Unity - 200s
  • Compile - 60s Total: 7–8 minutes

Cold Boot:

  • Boot - 200s
  • Launch Unity - 500s
  • Compile - 120s Total: 13–14 minutes**

We thought we could hide this underneath the user's "first message" so we introduced the logic of "ensure capacity" which would (like the orchestrator) launch a computer for the user on their first message, anticipating a full build. If we follow how long it takes to build a full game (roughly 400s) then in the warm flow we are left with 60s total (460s - 400).

The real win on this was after the computer launched iteration was instant (guarded only on actual compile time no webGL anymore).

The Final System:
Now we looked at all of this data and concluded that regardless of what we do, we have to compromise on some place. Either on the time, or on the quality or on the user experience.

But then we asked ourselves the question: "What do we have access to?" We came to the conclusion that every "compile time" even with WebGL was good enough **(1 minute, varying by CPU capability of the device). Render streaming, though it managed to make **compiling practically instant, made everything look quality wise worse. We didn't want to compromise on quality and playing experience so we agreed we'd go to the WebGL path. The only problem left then, was the browser downloading WebGL. We agreed that we'd figure that out at another time and left the problem there.

The next question was about how to "Run a Game Engine" for the user.

So we took the "renting GPU" idea *and we switched it to *"using the user's computer to run their own game". This meant that all the work (computation) was happening on the users computer which it was more than capable handling.

And it meant that the user owned everything related to their project on their own computer. Campfire was just the Agentic solution that modified the project and controlled the engine running on their GPU.

But then we came back to "experience" where we didn't want the user to "set up all this stuff" and you know the long walk with 20 setup features like how some apps do it. But then it hit us that we can absolutely entirely automate this process.


Picture Of User Downloading Campfire Bridge (one time download).


Picture Of Campfire Bridge Running On User's Computer Via Singular Command.

The user didn't have to run around in circles. They just had to download a singular 'Campfire Bridge' file on their computer, run a singular terminal command and that was it.

Campfire would handle the rest. Downloading what it needs, modifying project files accordingly etc. It wouldn't touch anything inside the computer, just a singular folder we allowed it to touch called "Campfire" where everything related to Campfire would be. It was perfect and it worked well and was easy.

This also added the benefit that we didn't have to rent any architecture. No computers no nothing because the users free GPU was right there.

Now if we conclude: We got "compile" down to a minute on the highest end and we automated the process of running the game engine to the point where the user wouldn't even see anything which is exactly what we wanted experience wise **so as to not overwhelm them. We completely left render streaming meaning that there were no latency or resolution or bandwidth issues. **We gave the user control over their own project.

All that was left was the tiny annoyance of browser download WebGL speed.

We looked at the process again and instantly understood what we were doing wrong. The real build file as specified previously was barely anything** (50mb). The problem was the network hopping **(computer to network to server to network to browser) when both the browser and the folder existed on the same computer.

Our fix for this was to change it so that the computer directly streamed to the browser via a secure TLS HTTPS server. **This gave us real **I/O disk speed (The same speed as copying and pasting something on your computer).

Conclusion: We managed to get the parts of Campfire that the user didn't need to know about, away from them. Managed to get them to own their own project and not cost us any architecture and got compile and iteration down to **under a minute **depending on the change.

There are a few limitations to this method **like the fact that now that the user's computer is running the game engine so we are left to the **mercy of their own specs. A normal computer can handle our game engine normally with 0 problems (you wouldn't even notice). If your computer is slower with less general specs then the compile time would just be slower, nothing else would change. So a user may have 20s compile and another may have double that at 40s. But overall it's a massive increase in fluidity compared to renting GPU Computers and waiting 13 minutes for it to do anything.

Note: We started writing this while still stuck on the rented-instance approach and it made us rethink our architecture. A few days in, we landed on the local execution model instead, which is the version we've dubbed "The Final System" and is what's currently implemented.

Top comments (0)