How I Made A Phasmophobia Companion App In 3 Days
Tags: reactnative, android, javascript, design
Published: 2020-10-10 07:39:52

![Phasmophonia Companion Promotional Image](https://i.imgur.com/kDN8S70.png "Phasmophobia Companion Promotional Image")

With a new semester of university starting in only a couple of days I decided to challenge myself to release 1 more side project before I was once again pummeled with assignments.

I had been playing Phasmophobia, a fun co-op horror game on Steam where you’re tasked with hunting and identifying ghosts. The game includes a journal which holds details about ghosts, allowing you to identify the correct type of ghost (Poltergeist, Demon, etc.) Phasmophobia released in late September and had quickly seen a surge of players and popular streamers trying out the game. It’s fantastic and I highly recommend every fan of horror to try it out with a friend or two, however, the in-game journal takes up the entire screen and is clunky to navigate.

Being a software engineer, I did what software engineers do, I decided to solve my problems by building an app. The purpose of Phasmophobia Companion (My app’s name) was to replicate this in-game journal on mobile with easier navigation, and smooth animations.

Keeping in mind that the game had just come out and that early access games have a short popularity lifespan I knew I had to hurry. Luckily there were no Phasmophobia related games on the Play Store, however rushing production posed an interesting challenge, every day it took to develop the app could’ve been a day when a competitor released their own version. Phasmophobia Companion wasn’t supposed to be a polished product, rather a quickly churned out solution to quench the market’s thirst. 

# Day 1
I spent most of Day 1 designing the app in Figma and looking at React Native carousel libraries (more on that later).

My design mockup in Figma:
![The App's Design in Figma](https://i.imgur.com/e9LWmjm.png "The App's Design in Figma")

In the spirit of developing under time pressure, I chose to stick to a design I was already comfortable with. That being mainly using cards that the user can swipe left or right to progress. I decided to replicate the game’s journal look and feel using a yellow old paper texture. While looking for a “spooky” font I found Shadows Into Light, which I thought matched the game’s theme, this would end up being used for the titles of each card, the app’s icon, and promotional material.

One of the key functionalities for Phasmophobia Companion was easy navigation. The in-game journal only included arrows to turn pages left or right, and a double arrow on the first page to skip to the last and most important page. What annoyed me most while using the in-game journal was how difficult it was to navigate from somewhere in the middle of the journal to the start or end. I decided for the journal in Phasmophobia Companion to be split into multiple sections, with a navigation button for the start of each section. There are 4 sections: Introduction (basic game mechanics), Ghosts (detailed descriptions of each ghost), Items (explaining which items gather what evidence), and Evidence (where the user can narrow down the type of ghost they’ve encountered).

The in-game journal:
![Phasmophobia's Journal](https://i.imgur.com/fvcTB2E.jpg "Phasmophobia's Journal")

# Day 2

As much as I value drawing up the design for each of my apps before starting coding, I had felt as though spending an entire day on designing the UI and UX swallowed up a lot of my time. I had to finish the app’s core functionality today.

I decided to use React Native because it’s the technology I’m most comfortable with, however after recently creating my first app without using Expo I was impressed with how small a vanilla React Native app can be in size. As such I chose to not use Expo (it wouldn’t have saved me much time anyway…)

After creating a basic card component modeled after Day 1’s design I had to figure out a carousel solution. There aren’t many carousel libraries for React Native, the most popular one react-native-snap-carousel hasn’t been updated in a long time and still used vanilla Animated instead of a library like Reanimated. However, I had previously used react-native-snap-carousel in a project (Mushroomology) and thought the results were good enough. After all, I didn’t have time to create my own carousel in Reanimated.

So I settled on react-native-snap-carousel. Next, I had to actually fill out the data for each card, this took quite some time as I had to type it out manually. Halfway through typing the entire journal out I thought of using an image-to-text program to save time, however, I was too stubborn to change at this point. Next, I created the Bottom Bar component, I had to pass the carousel’s ref to this component in order to navigate programmatically. For the icons, I used react-native-vector-icons, an amazing library that I use in pretty much every one of my apps.

By the end of the day I had pretty much completed the core functionality of the app, the only thing left was to create the last page. On the last page, users can fill out the evidence that they’ve gathered in order to determine what kind of ghost they’re dealing with. By far the most complicated page required more logic than simply showing text on the screen.

# Day 3
Determined to submit the app to the Play Console today I quickly got to work finishing up the app. I had to finished the last page’s logic. On the last page, users fill out three pieces of evidence or leave the slot blank. Each ghost has 3 pieces of evidence, the combination of which is unique to that specific ghost. For example, a Spirit’s evidence is the Spirit Box, Fingerprints, and Ghost Writing, while a Wraith’s evidence is the Spirit Box, Fingerprints, and Freezing Temperatures. If the user fills out the evidence as Spirit Box, Fingerprints, and a blank slot, both the Spirit and Wraith are possible ghosts the player has encountered. 

Evidence Page: ![The Evidence Page](https://i.imgur.com/hvqNA6h.png "The Evidence Page")

I created an array of Ghost objects with their corresponding evidence and names. I created a separate array filled with only the ghost’s names which I filtered on every render using the following function: 

```javascript
// Filter out the ghost list
// evidenceArr is the list of selected evidence
    ghostPickerList = ghostNames.filter((ghostName, index) => {
      let passes = true;
      evidenceArr.forEach((e) => {
        if (e > 0 && !ghostList[index].evidence.includes(e) && index > 0) {
          passes = false;
        }
      });
      return passes;
    });
```

With the final page working correctly I was almost done! I only had to create the app’s icon, splash screen, and promotional material, all of which I made using Figma. In order to implement the splash screen I used react-native-splash-screen.

Finally I was done! The only thing left to do was to submit the app to the Play Store and wait for it to be approved.

# Reflecting
Creating Phasmophobia Companion was an excellent learning experience and fun project to complete before the semester starts. The time pressure forced me to make decisions that I would not have made otherwise, for example using react-native-snap-carousel instead of implementing my own carousel in Reanimated. Because of this, my app suffered in build size, and in performance on low-grade android devices. In the future, I would like to create my own carousel library or contribute to react-native-snap-carousel to increase its performance. Not only would this improve the performance of Phasmophobia Companion, but it would also save development time in the future in the case of another time-pressure project. 

As for Phasmophobia Companion itself, I have shared my project with the community and am waiting for feedback on what to improve and which features to add.

You can check out Phasmophobia Companion on the [Google Play Store](https://play.google.com/store/apps/details?id=com.phasmophobiacompanion), and you can check out my messy rushed spaghetti code on [Github](https://github.com/Redseb/phasmophobia-companion)!