
At the end of my own playthrough, I uploaded my shots and logged onto the site to see them.

But one of them came out… weird.

Strange distortion! And are those bits of QR codes in the corners? Could this be the beginnings of an ARG?!
I posted the Firewatch.camera link to r/Firewatch, a discussion board for the game on Reddit, to see what the resident internet detectives could make of it.
Redditors sprang into action, loading the weird pic into Photoshop, filtering and cutting and turning it about in hopes of shaking out a secret.
My hunch was right—that was a QR code! Cleaned up and put back together, they found it led to…
Okay, maybe that's not exactly how it all happened.
Truth is, as I stared at the photo upload screen in a post-game slump, I got to wondering whether I could send in any old photo from my PC and have Panic print it off with the rest of the batch. I figured the game program probably sent the files to a hidden HTTP endpoint on firewatch.camera, so I broke out Charles Proxy, my tool of choice for HTTP interception. I like it because it’s easy to set up and supports SSL decryption, but other tools like mitmproxy could probably do the job as well. Looping back through the ending sequence again, I watched as my photos were retransmitted to The Cloud—this time with Charles listening in.
Charles picked up several requests to endpoints on https://www.firewatch.camera/api/v1/roll/create/form
, with cURL on the command line:
curl -H "User-Agent: UnityPlayer/5.2.4f1 (http://unity3d.com)" \
-H "X-Unity-Version: 5.2.4f1" \
-X POST --data "email=bob@example.com" \
https://www.firewatch.camera/api/v1/
-H
flags let us set custom headers so our request looks like it’s coming from Unity, the engine the game was built in. The next line sends along an email address (to which Firewatch Camera will send our photos page) with the HTTP POST request. Further experimentation revealed that this field can be left blank.As luck would have it, the server was happy to handle my request, responding back:
EvergreenBasinDrive
My original Firewatch.camera page appeared at https://firewatch.camera/SoftAcadiaCamp/, so I inferred that
create
gives back a unique key for the new set of photos.Mimicking the flow that Charles mapped out, the next step was to send in a JPEG image to
roll/EvergreenBasinDrive/upload_photo
:curl -H "User-Agent: UnityPlayer/5.2.4f1 (http://unity3d.com)" \
-H "X-Unity-Version: 5.2.4f1" \
-X POST -F index=17 -F photo=@firewatch.jpg \
https://www.firewatch.camera/api/v1/
The game submits photos in reverse order, with an index parameter starting at 17 and counting down to 0 for a full batch. (It won’t take more than 18 photos total—I’ve tried.) The -F photo=@firewatch.jpg
tells cURL to attach the contents of firewatch.jpg on my system as a file upload called "photo".
In short order I heard back from the server again:
{
"status": "OK"
}
roll/EvergreenBasinDrive/complete
. Again matching the Charles log:curl -H "User-Agent: UnityPlayer/5.2.4f1 (http://unity3d.com)" \
-H "X-Unity-Version: 5.2.4f1" \
-X POST --data="success=1" \
https://www.firewatch.camera/api/v1/
{ "status": "OK"
}
Presto! My custom photoset was finalized and visible at https://www.firewatch.camera/

Some more playing around along these lines let me suss out the remaining requirements and error cases in the API. I bundled this knowledge into a plug-and-play Node.js library and command line tool as a little demo.

With that all done, we can get back to the original joke! Inspired by the internal name roll
(short for "camera roll") used in the API, I:
- grabbed my real Firewatch pics from their Firewatch.camera page,
- made a Rick Roll QR code,
- whipped up the fake, distorted shot in GIMP, splitting up the QR code and hiding it inside, and
- used my new tool to upload it to a new Firewatch.camera page, alongside some of those photos I actually took in game.
All that remained was to stick the link on Reddit and let the fun unfold. 😉