OUTSIDE PARTIES horror scavenger hunt across a 1442-megapixel HDR image

With the story done, I’m at the stage of building all the art and dialogue to execute it. But I also can’t help continuing to refine the imaging engine... As if it wasn’t complex enough already, I’ve added one more layer!

When the brightness is cranked too light or too dark for the region you’re viewing, large areas can go to fully black or white. That’s OK most of the time—but if you ALSO zoom in really far (32x or 64x) then those areas can be really large: you can sometimes get a fully empty black or white screen, or close to it.

At that point, you can’t even see when you pan/scroll with the d-pad. No big deal, obviously you need to crank the image brighter or darker! But I just I didn’t like the look and feel of those blank open areas.
_

Solution: add a little grain to the image, only visible at the highest zooms.

It uses kDrawModeXOR so it works on any underlying graphics.

That way there’s a hint of texture you can see scroll. It also just looks nice I think! And it doesn’t harm the image: zooming in that far is mainly for fun anyway, and the grain is pretty slight.

But I didn’t want repeating grain. If it’s all/most of what you’re looking at at those moments, then I didn’t want you to see the same cluster of dots keep going by at the same interval all lined up in a row.

Making the scrolling grain image large is a start: twice the dimensions of the screen. But it would still repeat, just less obviously.

To test this, I temporarily added a bold test doodle to my grain image. (It’s actually a quarter of the full grain image but I repeated it 4x.)

grain without flips

That’s the repetition I did not want.
_

Solution: randomized grain.

Originally I was going to randomly flip each instance of the grain, and/or have a table of multiple different grain images (as if I’m not pushing RAM enough already). There would still be entire screens of repeating grain sometimes, but much less often.

That’s easy—just pick a random index 1 to 4 from this table:

flips = {gfx.kImageUnflipped, gfx.kImageFlippedX, gfx.kImageFlippedY, gfx.kImageFlippedXY}

But luckily, since my engine only redraws the newly-revealed portion of the screen as you pan, I have a way to do even better: randomize the flip of each new strip that is rendered.

grain with random flips

Now you’re talking! One source image, endless random grain.

Just one problem: as you pan, my engine pre-renders a whole range of different brightness levels. That’s what keeps cranking fast and smooth. But it means you now have random grain that changes as you tune the brightness. This gave the whole screen a distracting animated “sparkle” effect. It looked neat, but it looked very wrong.
_

Solution: deterministic pseudo-random grain.

I thought, what if I generated the flip number 1 to 4 from the pan coordinates with no random component?

Modulo (division remainder, the % operator) can easily turn any positive number into a number 0 to 3. Add 1 and I have my flip table index that changes with panning on either axis:

flips[(viewX + viewY) % 4 + 1]

No more sparkle! But how does it look?

It turned out the coordinates alone produced a repeating result horizontally (just the luck of how the numbers happened to divide). All I had to do to fix that was add a fixed integer (say, 3) to the coordinate sum before the modulo.

Then it looked random! However, the grain also did not change when zooming between 32x and 64x. Again, it felt wrong.

So in addition to a fixed integer, I also added the index of the current zoom level. Now there was a non-repeating result that looked random, didn’t sparkle, and was unique at different zooms.

flips[(viewX + viewY + 3 + zoomNum) % 4 + 1]

Success! Completely deterministic, fast, and with repetition being infrequent enough to satisfy me.

grain with deterministic flips

Remove the test doodle, and here’s the grain alone:

grain alone

Two more imaging engine enhancements:

  1. You can now scroll past the top/bottom of the panorama—making it more intuitive to zero in on targets near the edges.

  2. You can now zoom out farther, to .25×. (Those zoom numbers are simply the “number of screens tall” the panorama is, so this new zoom level fills 1/4 of the screen height.)

So now you can see the ENTIRE “psychopanorama” at once. (A) and (B) to zoom.

max to min zoom

At min zoom, the panorama is 55x400 pixels, less than 1/4 of the total screen (since the 20px bottom UI doesn’t count).

At max zoom, the panorama has the area of 7.5 ping pong tables, or 20 standard interior (30-inch) doors.

Most of my current work on this (in and among my clients’ tasks!) is too spoilery to relate, but there are some things I've been posting on Mastodon that I’ll share here too:

18th century Italian physicist Cristina Roccati made endless sketches and inkblots of “le sfere nere”—strange “black spheres” that terrified her. Her occult secrets were buried with her (literally). But they didn’t stay buried. The secret organization that claimed both her methods and her name used the sfere nere as their emblem. The Cristina Roccati Institute still exists today, exploring things better left alone. But to this day, even the CRI doesn’t know… What are the sfere nere?

Every model of Hellscryer since the H series is bright yellow so it can be located quickly in near darkness or in altered states. K-series units now use unlighted displays, since artificial light can attract unwanted attention from outside parties. Each Hellscryer is locked by means of a 3-digit passcode. DO NOT BE LAX ABOUT SECURITY. The Hellscryer is extremely dangerous in untrained hands, and an easily-guessed passcode could put non-CRI individuals at risk if a unit accidentally comes into their possession.

The contemporary version of the CRI logo appears on the unlock screen:

OUTSIDE PARTIES Passcode

And lastly here’s a launch animation:

OUTSIDE PARTIES launch sequence

I haven’t been posting many details here (I’ll write up more after release!) but here’s something kind of fun—how I keep the voices and text ticker in sync…

Each found item has a voice signal that plays, accompanied by a text ticker at the bottom of the screen. Both the voice and the text “unscramble” together as you tune the image in using the crank.

The text is just a single long string in a table. But it stays well enough in sync with the voice by two very simple means:

  1. Each long string is paginated programmatically into one-line ticker “pages.” (Some pages are longer than others, depending on how many words fit.) Then every letter is assigned to equal one generic “time unit,” and those time units are distributed into the duration of the WAV audio. So a page with 8 letters will appear for half the time of a page with 16 letters. That gets the timing pretty close to the voice, regardless of speaking speed.
  2. But I can do better: listening to the audio, I add punctuation that matches the voice. (This may differ from the punctuation in my original script, depending on how the voice actor performed it.) The paginator, as it adds up the “time units” per page, adds EXTRA units for punctuation. Periods especially—and ellipses are converted to runs of periods, so they add the most time. Simply by punctuating the text, which is good for readability anyway, the timing matches the voice much better.

It’s not perfect TV-quality sync, but it’s pretty good! I also add in a little extra time before the voice and its transcript loop and play again.

At the same time as the pagination is processed, the LAST words of the signal are automatically pulled out to become the visible title of each item in the list. All dialog is written so that the final spoken words describe the item to find.

So this…

I THINK I JUST GLIMPSED THE ROPE MAN FOLLOWING ME AGAIN. IF HE'S ONE OF THE PASSED HE IS DEFINITELY NO MINDLESS SPIRIT... I'M HOPING IF I FOLLOW A STRAIGHT LINE I CAN TRACE THIS PIPE.

…is broken into pages, but the last page might have just one or two words. That would not be a reliable “mission title.” Instead, the generated title is:

…I CAN TRACE THIS PIPE

(With final punctuation always stripped away, and ellipsis added.)

Also, here are some paravultures and a horse eating a lizard.

A lot hasn’t changed since the first post above!

But some things have. The device is not called a Panopticon K5, but a Hellscryer K5.

There’s a list of all targets called the Signal Log. (A list of 1, at first, then more and more are unlocked and get added.) You can select a target you haven’t found to go searching for it, or you can select one you already found if you want to look at it again.

So the list contains both things you’ve found AND things you haven’t. And just stepping through the list with up/down makes the audio signal for each object play—with subtitles in the ticker across the bottom—so you can easily review the story events if you wish. (But the clues to new targets are not story-dependent: you won’t have to go back and review just to make progress finding targets.)

I needed really clear icons to distinguish open targets from ones already acquired. Here’s the system I came up with:

Open target to find:

badge-open

Already-found target (which matches the full-screen crosshair animation that happens when you find something):

badge-found1-table-23-23

The open target icons are bright and bold, so it’s easy to scroll through the list and spot any items you haven’t yet acquired.

But I found I needed a LOT more icons than just those two: I wanted to indicate “multi-targets” such as “5 salt runes” and show a number counting down.

So there’s this variation counting down how many are left:

badge-3

Additionally, sometimes you find an object whose voice audio does NOT describe a new target—just a bit of story. (Some “branches” of the quest have to reach an end, or the game would be infinite!)

It didn’t make sense for those “dead-end” items to receive the usual “target” icon when added to the list, because there’s nothing to find! But giving them the “already found” crosshair felt weird too, becase you didn’t... find anything. At the same time, I wanted an icon that looked similar to “aready found” so it wouldn’t catch the eye while scrolling the list on the hunt for open targets.

I came up with a “dash” icon that also happens to be half of the crosshair (along with a matching full-screen animation of horizontal lines when you first acquired the “dead end” item):

badge-none

I thought I was done... but I wanted multi-targets to look different in the list even after being fully acquired. So here’s the icon for a set of 5 items once you’ve found them all:

badge-found-5

Having the one big bold crosshair helps it feel similar to the normal single-crosshair icon.

But it also lets you cycle through the whole set for viewing previously-found targets. With the target line selected (using up/down), you can hit d-pad left/right to cycle through the set. Here are the three cycling icons for a set of 3:

badge-found-3x3

I made icons for multi-target sets of every size up to 6.

Lastly, after the game is over you can still freely revisit the whole list, and view or listen to anything you want. Since the list still exists when there’s nothing more to find, I thought the final target deserved its own icon.

It’s the logo of the Cristina Roccati Institute, as seen when you launch the game:

badge-sfere

Still mostly black, still distinct from the bold white “open target” squares. So despite all the icon variations, it still feels like just two “kinds” of icon: things to find (bold and white) and things already-found (slight and black).

The game is out! (I do have more to log about the development process in future, but my first priority is a searchable walkthrough/hint site.)

The final game has 150 targets, and professional voice acting for each one.

https://play.date/games/outside-parties/

I’ve made a couple of updates since launch:

11/25 v 1.1.0:

  • Fix for an occasional bug that could play the wrong audio (not matching the text)

  • Quieter background ambience during voices

  • Slightly quieter sound while raising the device in front of you to start each session

  • Boosted treble to make a couple specific voice clips stand out better (they had been lacking treble compared to most)

  • Less stereo panning of certain background sounds (a potentially annoying effect with headphones)

  • More variation in background sounds

  • Removed unneeded target bar in Void Monitor mode

  • Less-frequent music in Void Monitor (now the same as game play)

  • Fixed unwanted occasional clear voices in Void Monitor

  • Fixed situation in which game view could shift to top of pano upon starting a new game

  • Addressed a very rare crash when zooming with B

  • Unique icon in the Log list for the very last target once found

  • Misc. artwork tweaks to make certain things stand out a little better etc.

Today’s v 1.2.0:

  • Dozens of additional random background sounds (only about 2 MB app size increase)

  • New weighting system to better manage which sounds are more common vs. rare

  • New animated effects in home screen title card

  • New animated effects in home screen list view icon

  • Added skull to wrapping paper

  • Adjusted timing of text vs. spoken words for many signals

  • Misc. small artwork tweaks

  • Fix for potential crash when letting the Playdate auto-sleep during a popup dialog

  • Fix for some unintended voice fragments

  • Fix for Playdate sometimes failing to auto-sleep when left unattended with crank out

There will be at least one further update, adding a QR code for the searchable hint/walkthrough site I’m preparing. (If anyone finds a particular target to be especially difficult—unless that made it fun—I’d like to know!)

I’m also thinking of adding a few “invisible achievements”—never stated in-game, but appearing in your trophy case for those who use it. (I hadn’t even realized such an achievement standard existed on Playdate!)

P.S. The final app size came to 57.9 MB (half that when zipped). Not as bad as I'd feared, for around 1.25 hours of total audio and all that HDR imagery!

Just noting that I’ve add a “context” feature to the transcript screen, to help if you take a long break from the game and then want to recall WHERE a particular clue was given to you.

And I added the Mission Help QR code to the System Menu, pointing to the CRI corporate intranet... a.k.a. searchable hints site.

(750 hints available—you can get just as much help as you want and no more, without risk of spoilers. Essentially a walkthrough, but with only as much detail as you want. You’ll never accidentally “see ahead.")

I made a full final trailer, with sounds and images recorded from gameplay, visuals depicting some of the in-game lore, and some of the actor’s voice work (Mary McGahren):

Nice to see that my country (Italy) can be represented also by something different from “ Pizza e Mandolino” :wink:

(I’m afraid I did take some liberties with Roccati’s life though.... she studied all kinds of real science, but not the occult.)

I, too, often take poetic licence with reality... for example, I’m currently working on a comic set in my city – Bologna – populated by humans and anthropomorphic animals.

Bologna is, incidentally, the city where Cristina Roccati studied physics, logic and metaphysics, amongst other subjects.

Just wanted to link to a few reviews around the web. So nice to see my work (and this platform!) well received.

Engadget:

PlaydateUnofficial:

Destination Playdate:

YourCoolUncleMarisa: