PublicPersonal Project

Wakawaka

Wakawaka is an idle game for programmers that turns coding time into experience points, designed around the satisfaction of watching personal activity data accumulate over time.

Live Site

Tech Stack

  • Frontend: React, TypeScript
  • CSS Framework: Tailwind CSS, DaisyUI
  • Authentication: Google OAuth, WakaTime OAuth
  • Database: Google Sheets
  • Backend: Express.js, Node.js, TypeScript
  • Background Jobs: node-cron
  • Containerization: Docker, Docker Compose
  • Project Structure: Monorepo with frontend, backend, and Docker Compose setup
  • Deployment Platform: DigitalOcean
  • Web Server / Reverse Proxy: Nginx
  • DNS / SSL / Proxy: Cloudflare
  • Analytics: Google Analytics 4 (GA4)

System Flow

  1. The user signs in with Google OAuth.
  2. The backend creates or looks up the user record in Google Sheets.
  3. The user authorizes and connects their WakaTime account.
  4. The backend stores the WakaTime access token and refresh token.
  5. A daily cron job calls the WakaTime API.
  6. Coding time and language statistics are converted into character experience points and programming language skill levels.
  7. The frontend retrieves character, enemy, announcement, and server data, then renders the game interface.

Implementation Details

Using WakaTime as the Activity Data Source

The project uses WakaTime as its data source to retrieve each player's daily coding time and programming language usage.

More details are available in WakaTime Introduction.

The backend runs a scheduled cron job every day to call the WakaTime API, retrieve the previous day's coding activity, and convert the result into character experience points and language skill experience.

To avoid writing duplicate data for the same date, each character record also stores the last date on which experience points were updated. Before running the daily update, the system checks whether that day's data has already been processed.

Using Google Sheets as the Database

I chose Google Sheets as the data storage layer to reduce the initial development cost and to explore the Google Sheets API, which I had not used in a real project before.

One practical benefit of Google Sheets is that data can be inspected and edited directly. Even outside the development environment, it is easy to check player records, character data, and update history.

Compared with a conventional database, however, Google Sheets has more limitations around data structure, permission control, query efficiency, and API rate limits. These constraints need to be handled by the application layer.

As the number of players increased, the project eventually hit 429 Too Many Requests errors, which led to further changes in how data was read and written.

Monorepo Project Structure

The frontend and backend are managed in the same repository, with Docker Compose used to build and run the services together.

Since both applications are currently deployed on the same server and the backend only needs to run a single instance, I did not split them into separate deployment pipelines.

This structure keeps the frontend, backend, and deployment configuration in one place, while reducing the operational complexity of maintaining a personal project.

Google OAuth Login

The website uses Google OAuth as the main sign-in and registration method, so users do not need to create a separate account and password.

When configuring the Google OAuth consent screen and publishing the application, the following public website information is required:

  1. Official website homepage
  2. Privacy policy
  3. Terms of service

After OAuth succeeds, the backend uses the user information returned by Google to create or look up the corresponding Wakawaka user record.

WakaTime OAuth Authorization

Google OAuth is responsible for Wakawaka account login, while WakaTime OAuth is used to obtain permission to read the user's coding activity data.

After authorization is complete, the backend stores the access token, refresh token, and expiration information provided by WakaTime. During the daily update process, the system calls the WakaTime API with the access token. If the token has expired, the refresh token is used to obtain a valid access token again.

Separating login from coding data authorization lets users create a character first and decide whether to connect WakaTime afterward.

JWT Login State

After Google OAuth succeeds, the backend issues a JWT. The payload only stores the basic information needed to identify the login state, and the token is stored in the browser as an httpOnly cookie.

The frontend cannot directly read the cookie through JavaScript. Instead, API requests are sent with:

credentials: 'include';

The browser automatically includes the cookie in the request. When the backend receives the request, it verifies the JWT with JWT_SECRET, then uses the user_id from the token to query the actual user and character data from Google Sheets.

Compared with storing tokens in localStorage, using an httpOnly cookie reduces the risk of the token being directly accessed by frontend JavaScript or through an XSS attack.

The cookie is also configured with SameSite and Secure settings to reduce risks related to cross-site requests and non-HTTPS transmission.

429 Too Many Requests

The project first encountered 429 Too Many Requests when there were only a little over 40 registered players, which was earlier than expected for hitting the Google Sheets API request limit.

The main issue was that the daily update job performed multiple read and write operations for each player within a short period of time. As the number of players increased, the accumulated API requests could easily exceed the limit.

The update process was adjusted in two main ways:

  1. Individual reads and writes were changed to batch reads and batch updates.
  2. Delays were added between batches to avoid sending too many requests in a short period of time.

After these changes, the daily update job has been able to process data for more than 200 players reliably.

Project Outcome

  • Converted real WakaTime coding activity into character growth data inside the game.
  • Used Google Sheets as a lightweight database to reduce initial development time and support manual maintenance.
  • Managed the frontend, backend, and deployment setup in a monorepo, with Docker Compose used for deployment.
  • Integrated Google OAuth and WakaTime OAuth to complete login, account creation, and coding data authorization flows.
  • Used JWT and httpOnly cookies to maintain login state.
  • Automated daily player data updates with a cron job.
  • Changed Google Sheets API operations to batch processing to solve the API rate limit issue that appeared as the player base grew.
  • As of July 2026, 208 players have created characters.