project screenshot 1
project screenshot 2
project screenshot 3
project screenshot 4
project screenshot 5
project screenshot 6

Vibecheck

Vibecheck is a mobile turn-based RPG game that turns any casual encounters into your in-game ally.

Vibecheck

Created At

ETHOnline 2023

Winner of

🥈 Tableland — Best Use of Studio

Project Description

Vibecheck is a Spotify, GPS-powered turn-based mobile RPG.

About the concept

The concept is very simple: sign-in with Spotify, turn your geolocation on, create your character and let the app work its magic !

When your geolocation is activated, every 30 seconds Vibecheck starts sharing with other app users in a 30 meters radius the last song you listened to on Spotify. If another app user crossed your path, you instantly exchange your last Spotify song along with your in-game character.

This data exchange is triggered instantly and in a completely anonymous manner: no one knows who is who, who received what and where/at what time they crossed path. This ensures the privacy and safety of all app users.

There's an in-game consequence to crossing path with the same user multiple time: the more you cross this user, the more their character will become in your game - and vice versa !

About the game

Concerning the game aspect, Vibecheck is a turn-based role-playing game: during combat phases, each players (typically the user and the AI) decide what actions to perform during the round, which is then executed based on each characters' statistics. Those statistics influence how well a character performs in various aspects of combat, and include like attack, defense, strength, stamina, stealth, intelligence, speed, wisdom, reputation, speechcraft, and luck.

During a round, a character can perform an different type of moves: attack, defend and use an object. Those moves can or cannot be executed based on the character's equipped weapon, their position in the battle formation. Furthermore, the action's rate of success is defined by the statistics mentioned below : an attack will use the attack statistics along with a modifier like strength, intelligence, stealth... depending on the character's class. The outcomes of these basic moves are also influenced by factors like luck, the party's "vibe" (harmony), the character's remaining health point, the number of rounds in the battle and the character's stamina (a character with less stamina will perform more badly the more turns it takes ; or an attack will be more likely to either fail or succeed if the user has low health points).

For more potent and unique actions, characters can employ special moves. Special moves leverage the character's special stat, and, like basic moves, their efficacy is augmented by modifiers relevant to their class.

In addition to individual character attributes, the party's collective strength is significant. Parties have a leader and an inventory of items. They also accumulate Vibe (the harmony within a party) and Artefact points. Vibe impacts the likelihood of a successful vibe attack and the effectiveness of using items or skills on fellow party members (for instance, in party with a negative vibe stats, a healing action on a fellow party member might fail due to low harmony). Artefact points grant the party access to powerful, unchangeable buffs (if they have the artefact in their inventory and enough points to equip it) that can influence the outcome of battles. Artefacts cannot be changed during a battle, but can be switched anytime outside of battles.

Why Vibecheck ?

Vibecheck was thought to be a way to promote the use of public transports: a lot of users either listen to something or play something on their phone in public transport. Vibecheck can insert itself perfectly there, as there's an in-game incentive in crossing path with many other users to get more powerful in-game characters.

How it's Made

List of services and apps

Vibecheck is built on top of multiple services and apps :

  • The main webapp built with Astro, HTMX, TailwindCSS and Cometh, used to authenticate, play the game, and check vibechecks ;
  • The companion mobile app (built with Expo), which is in charge of communicating the user's geocoordinates with the geolocation service;
  • The user management service, built with Pocketbase under the hood and storing Cometh wallet address ;
  • The geolocation service, built with Supabase under the hood ;
  • The central API - a REST API built with Hono that connects all other services and apps together and that provides data encryption with Lit Protocol

Overall Technical description

Upon their first sign-in in the app, the user is invited to sign-in using their Spotify account. Once signed in, a new wallet is created for them via Cometh. We associate the user (identified with a random ID) and their wallet in Pocketbase, which we use as our self-hosted authentication service. Absolutely no identifiable information/sensitive information (Spotify username, email, Spotify ID, phone number...) is stored to guarantee and ensure anonymity, security and privacy. Only the user can update their data, and only the admin has a READ access to it.

The user's last listened song is instantly fetched from the Spotify web API and stored as the user's status in Pocketbase. Every 30 seconds, the webapp will check if the user listened to something new, and if so, update the status in Pocketbase.

The mobile app acts like a podometer of sorts ; if your geolocation is enabled and if you grant permission to the app, every 30 seconds, it will save your geocoordinates in a database until you disable your geolocation ;

Those geocoordinates are stored in Supabase, a PostgreSQL database. Every time a new geocoordinate is added to the database ("passersby" table), it instantly verifies if another person that uses the app crossed path with them (the 2 users are within 30 meters of each others in a 20 seconds time frame). This is possible thanks to a TRIGGER on the table that on every INSERT operation calls a function that performs this verification. Those geoqueries are possible thanks to the PostGIS extension.

The passersby table is scheduled to be emptied every minute via a CRON (the passersby data will be deleted if they were created more than 31 seconds agon), and the users are only referenced by their wallet address. In addition, only the admin is allowed to read and write this database to ensure maximum privacy.

If indeed users crossed path, a new encounter is created in the PostgreSQL database (encounters table).

Whenever a new encounter is created in the PostgreSQL database, a webhook is setup to call the /vibechecks endpoint of our REST API. This endpoint is in charge of creating an anonymous vibecheck in Tableland. This anonymous vibecheck contains the status (stored in Pocketbase) from the 2nd user. This message is encrypted via Lit Protocol, and can only be read by the first user in the webapp.

Under the hood, only 1 wallet is authorized to decrypt those messages: the reader wallet. The webapp (which is server-rendered), by verifying the active user session (from Pocketbase and Spotify), can determine if the current user is allowed to decrypt the message (if the wallet_public_address field from the current Pocketbase session is equal to the receiver field from the stored row in Tableland.) If so, the user can decrypt the vibecheck and see what song they came by.


Game

When the user first signs up, their last 30 songs from Spotify are used to generate their character sheet using OpenAI.

A character sheet has the following immutable attributes: id (automatically generated, unique), a name, a bio, an avatar and a class. The following attributes are statistics, which can evolve: health, special, attack, defense, speed, strength, stamina, stealth, agility, intelligence, wisdom, reputation, speechcraft and luck. The character sheet is then stored in Tableland.

In addition to the character sheet, after signing in and creating their character sheet, the user will have created for them automatically their actual character, their party and their party inventory, which all have their tables in Tableland.

A character extends a character sheet, and has the following additional attributes: level and experience.

A party has a leader (the user's character) and 2 different statistic points: artefact points and vibe.

In addition to the leader, a party can have other characters associated to it. Those are party members, and are a reference to characters. Those characters are actually a local copy of the character sheet created by other users that the current user encountered.

A party also has an inventory, which is stored in its own table on Tableland.

To sum up, the game relies on the following tables:

  • characters_references
  • characters
  • parties
  • party_members
  • party_inventories

Upon their first sign-up, the user gets their character_reference, and a character that uses that reference, along with a party and a party inventory ; whenever they cross path with someone, if they didn't cross path with that person before, they get issued a character that uses the character_reference of that user, and this character is referenced in their party as a party_member ; if they already crossed pass with that user before, then the character will see their stats boosted.

Depending on how the user reacted to the vibecheck, the vibe of their party increase or decrease, which has an impact on how combats will unfold.


Technical difficulties and compromises

Ultimately, I came out with this stack and flow mostly on compromises.

I initially planned to build a single progressive web application but the encounter detection can't be build with the current state of PWA. The first version I envisioned would have been built using BLE (bluetooth low-energy) to exchange data between users instantly (like those COVID-19 tracker apps) ; however, it's not possible yet to build this with the web.

I then looked into React Native, but it seems this flow (instant permissionless data exchange via bluetooth) is not possible to implement there yet either. This meant writing a native app, which I don't have the skills for.

I compromised with using geolocation instead but faced another issue: background geolocation is not working on PWA either. So I decided to build with Expo/React Native.

I then faced another issue: some of the sponsors' tech I wanted to use (Push Protocol, XMTP, Cometh) was either not compatible with React Native, or I needed to build my app (with expo prebuild) ... which was not working on my machine. After some investigations, I discovered that my Java version (21) is not supported by Gradle, which in short meant I could only use Expo Go to continue building my app - or had to deal with reinstalling my environment, which could be very time consuming.

With these problems in mind, I had 2 possible solutions :

  • Build the app entirely on the web and render it in the Expo app in a webview ;
  • Build 2 different applications : a companion app in charge of the geolocation, and a main web based app that allow the user to sign-in, set their online identity (wallet), play, and access their vibechecks.

I decided to go with option n°2 and started to rebuild everything during week 2. I still faced some difficulty with sponsors' tech. Initially, I wanted the user to see their vibecheck as a message in their wallet, but I couldn't quite figure out how/if I could have Cometh wallet work with XMTP and Push Chat, so I came up with a different implementation : encrypt the message content with Lit Protocol and store this encrypted message on Tableland.

I was a bit surprised at how difficult it was to use Lit Protocol in NodeJS, and I couldn't get the Cometh wallet to decrypt the message, so I had to instead have a dedicated reader externally owned wallet, which would, based on the current user session, decrypt the content.

background image mobile

Join the mailing list

Get the latest news and updates