# WorldGuessr: an open GeoGuessr-style game on the Street View Embed API

> WorldGuessr is a React and Next.js geography game where you guess the country from a Street View panorama, with multiplayer, country streaks and a free-to-self-host source tree. Two things constrain it: the PolyForm Noncommercial licence, and the fact that the free imagery comes from the Street View Embed API rather than the paid SDK the original game uses.

**codergautam/worldguessr** — Free & Open source version of Geoguessr

- Repository: https://github.com/codergautam/worldguessr
- Website: https://worldguessr.com
- Stars: 557 · Forks: 195
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/codergautam-worldguessr

## A clone with a different imagery budget

WorldGuessr describes itself as a free-to-play, source-available version of the popular geography game inspired by GeoGuessr, a React based project meant to provide a fun and educational way to explore the world through Google Street View imagery. The live site is at worldguessr.com and there is a Discord community linked from the README.

The distinguishing claim is in the fourth bullet of the feature list, and it is the reason the project can be free where the original is not. The source code is available and free to self-host for noncommercial use, and the game uses the Google Maps Streetview Embed API, which the README says is completely free compared to the costly SDK used by GeoGuessr. The acknowledgements section names the Google Maps API as the source of the generous free tier on street view imagery.

That distinction has consequences beyond price. The embed API is designed for showing a panorama inside a page, not for programmatic Street View metadata, so a project built on it inherits the limits of that API rather than the full SDK. It also means the free tier's quotas apply, and a self-hoster running a popular instance is working inside those limits without a paid arrangement.

The other three features are the game itself. Random Street Views give a new location anywhere in the world on each game, Multiplayer Mode lets you challenge friends or play against random opponents in real time, and Country Streaks asks how many countries you can guess in a row.

## A location generator that balances the country distribution

The repository layout shows that this is a full application rather than a demonstration, and the directory names describe the architecture more precisely than the feature list does.

The map generation lives in mapgens/ and the location balancing is attributed in the acknowledgements to Vali by SlashP, used for generating balanced locations distributions for all countries. The point of that is straightforward and important for a guessing game: a uniform random pick over the globe would land in the ocean or in a country with no Street View coverage most of the time, and the resulting round would often be unwinnable. A balanced distribution keeps the odds roughly fair across countries.

Around that sit the rest of the parts. The Next.js pages are in pages/ and the React components in components/, with the shared utilities in utils/, lib/, serverUtils/ and shared/. The backend is split across api/ for the HTTP API, ws/ for the WebSocket server, models/ for the data models, server.js and authServer.js as the two Node servers, cron.js for scheduled work, and workers/ for background processing. Authentication is separate from the main server, and there is a clientConfig.js at the root alongside next.config.js.

There is also data/ for the country and location data, embed/ for the embeddable pieces, a mobile/ directory, and a test/ directory with vitest.config.js, so there is a test runner configured. The presence of AGENTS.md, PRODUCT.md, DESIGN.md and a mobile-team-parity-plan.md at the root says the project is developed with some structure around it, and a .replit file alongside replit.nix points at Replit as a development environment.

## Running it locally, and the four processes the dev script starts

The prerequisites are three runtimes, and pnpm is not optional.

- Node.js (v18.18 or later)
- npm (v6.x or later)
- pnpm (v9.x or later)

The three installation steps are clone, install and run.

```bash
   git clone https://github.com/codergautam/worldguessr.git
   cd worldguessr
```

```bash
   pnpm install
```

```bash
   pnpm run dev
```

Then you open http://localhost:3000 in a browser.

The reason pnpm is mandatory rather than preferred is visible in the package.json. The preinstall script is `npx only-allow pnpm`, which fails the install if you reach for npm or yarn, and the dev script is a `npx concurrently` invocation that launches five things at once: the WebSocket server under nodemon watching ws/ws.js, the main server watching server.js and the api, serverUtils, models, components/utils and shared directories, the auth server, and the cron job, with `npx next dev` running the Next.js front end alongside them. There is a welcome.js node script that runs first in the dev command, and each server is started with `NODE_OPTIONS=--no-warnings`.

That five-process shape is the single most important structural fact about the project, and it is also the reason the deployment section exists. A development machine tolerates four watchers and their memory use. A VPS does not, and the environment variables below are how the front end is told where those processes ended up.

## The environment variables a VPS deployment needs

The README is emphatic about this. If you are deploying WorldGuessr on a VPS or any server with an external IP, not localhost, you must configure these environment variables in your .env file, and the first two have to point at the public address of the machine because the browser has to reach the API and the WebSocket server directly.

```bash
# Replace YOUR_IP with your server's IP address or domain
NEXT_PUBLIC_API_URL=YOUR_IP:3001
NEXT_PUBLIC_WS_HOST=YOUR_IP:3002
```

The example with a bare IP address shows the same shape with a real address in place of the placeholder.

```bash
NEXT_PUBLIC_API_URL=123.45.67.89:3001
NEXT_PUBLIC_WS_HOST=123.45.67.89:3002
```

And the domain form, which the README notes is for after setting up nginx, is where the ports disappear because the reverse proxy terminates them.

```bash
NEXT_PUBLIC_API_URL=api.yourdomain.com
NEXT_PUBLIC_WS_HOST=ws.yourdomain.com
```

The NEXT_PUBLIC_ prefix means these values are compiled into the client bundle, which is correct for a public address and is a reminder that nothing secret belongs in them. The detail to notice is that the API and the WebSocket server sit on different ports, 3001 and 3002, so a deployment that exposes only one of them will have the game load and then fail to connect.

The README points at docs/environment-variables.md for the detailed documentation, and the repository has a docs/ directory at the top level.

## MongoDB, Google OAuth, and the three-part setup checklist

The quick setup checklist has three items, and each one is a service the project cannot run without.

The first is MongoDB, and the README asks you to create a cluster on MongoDB Atlas with the free tier available, then add the connection string.

```bash
   MONGODB=mongodb+srv://username:password@cluster.mongodb.net/worldguessr
```

The second is Google OAuth, with credentials created in the Google Cloud Console.

```bash
   NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
   GOOGLE_CLIENT_SECRET=your_client_secret
```

The third is the API and WebSocket URLs, which point at the external IP or domain as described above.

The split between the two Google variables is worth noting, because it is the same split the NEXT_PUBLIC_ prefix implies. The client id is public and lives in the browser bundle, while the client secret is server-side only and is used by the separate authServer.js process. Putting the secret in a NEXT_PUBLIC_ variable would publish it.

The MongoDB string is also written in the connection-string form with credentials inline, which is a reminder that a .env file holding these values should not be committed, and that the deployment needs a place to keep them outside the public directory.

## The build pipeline, the release naming, and what the licence allows

The production build is not a single next build. The build script runs two data-fetching scripts first, scripts/fetchMapCount.mjs and scripts/fetchStoreRating.mjs, then cross-env next build, and then scripts/writeSitemap.mjs. In other words, the build pulls external data, compiles the application, and writes a sitemap, which tells you the site expects a live connection at build time for the map count and the store rating.

There is also a set of variant scripts for other front ends, with dev:gd for game distribution, dev:poki for Poki, dev:duckmath and dev:6x, and a build:poki that hardcodes the production API host. The variant scripts set environment flags such as NEXT_PUBLIC_GAMEDISTRIBUTION and NEXT_PUBLIC_POKI, so the same codebase is being pointed at different deployments rather than forked.

The release names are the other oddity. The recent releases are not semantic versions but Coolmath Build 11, Coolmath build 10 and CoolMath Build 9, dated 2026-09-09, 2026-07-11 and 2026-05-01, and the package.json still records version 0.1.0 with private set to true. So the GitHub releases are deploy snapshots of a hosted instance rather than published library versions, and there is no published npm package to install.

The licence is the constraint that decides who can use this. It is distributed under the PolyForm Noncommercial License 1.0.0, and the README states you are free to use, modify, and distribute the project for noncommercial purposes, with the full text in LICENSE.md. That permits a school, a club, a portfolio project or a self-hosted instance for personal use, and it does not permit running it as a commercial service.

## Where WorldGuessr is the wrong choice

Three cases point elsewhere. The first is a commercial product. The PolyForm Noncommercial 1.0.0 licence is unambiguous, and the README says so in the same line that says the source is available, so a hosted paid instance or an ad-supported deployment is outside what the licence grants. A team that wants to monetise a geography game has to build something else.

The second is a project that needs the depth of the original game. The imagery comes from the Street View Embed API rather than the SDK, and the acknowledgements describe the free tier as the reason. That is what makes this project free, and it is also the ceiling: the round generation, the coverage and the quota behaviour are those of the embed API.

The third is a team that wants a single-process deployment. The dev script starts four watchers, and a production deployment is expected to expose two ports plus a Next.js server, a MongoDB cluster and Google OAuth credentials. That is a real operational surface for a game, and a team wanting one container running one process will find this project heavier than expected.

The alternative worth weighing is building the game directly on the paid Google Maps SDK, the approach the original game takes. The difference in approach is that the SDK exposes the full Street View metadata API, which is what makes country-accurate generation and richer round logic possible, and it comes with usage-based pricing rather than a free tier. WorldGuessr trades that capability for a zero-cost imagery source, which is why it has to solve the balance problem itself with a separate location distribution generator, and why its own feature set stays narrow.

## Conclusion

WorldGuessr fits a developer who wants to run a geography game on their own server, to change the mechanics freely, or to see how a multiplayer browser game is put together with a Next.js front end and separate API, WebSocket, auth and cron processes. It does not fit a commercial deployment, because the PolyForm Noncommercial 1.0.0 licence covers noncommercial use only, and it does not fit a project expecting the original game's coverage, since the imagery comes from the free Street View Embed API rather than the SDK. Before you build on it, read docs/environment-variables.md, set MONGODB, NEXT_PUBLIC_GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, and copy the NEXT_PUBLIC_API_URL and NEXT_PUBLIC_WS_HOST values, because the dev script starts four separate services on ports 3001 and 3002 that the front end has to be told about.

## FAQ

### What is WorldGuessr?

It is a free-to-play, source-available geography game inspired by GeoGuessr, built with React and Next.js, where you guess the country from a random Google Street View location. It adds a real-time multiplayer mode against friends or random opponents and a country streak mode.

### How do I run WorldGuessr locally?

You need Node.js 18.18 or later, npm 6.x or later and pnpm 9.x or later, then clone the repository, run pnpm install and pnpm run dev, and open http://localhost:3000. The install enforces pnpm through a preinstall script that runs npx only-allow pnpm.

### Which environment variables does a WorldGuessr deployment need?

NEXT_PUBLIC_API_URL and NEXT_PUBLIC_WS_HOST pointing at the public address of the API and WebSocket server, plus MONGODB with the MongoDB connection string, and NEXT_PUBLIC_GOOGLE_CLIENT_ID with GOOGLE_CLIENT_SECRET for Google OAuth. The README points at docs/environment-variables.md for the full documentation.

### Can I use WorldGuessr commercially?

No. The project is distributed under the PolyForm Noncommercial License 1.0.0, and the README says you are free to use, modify and distribute it for noncommercial purposes. The full text is in LICENSE.md.

### Why does WorldGuessr use the Street View Embed API instead of the paid SDK?

The README says the Street View Embed API is completely free compared to the costly SDK used by GeoGuessr, and the acknowledgements credit the Google Maps API for the generous free tier on street view imagery. That choice is what allows the game to be self-hosted at no licence cost, and it is also the limit on how the round generation can work.

## Sources

- [codergautam/worldguessr on GitHub](https://github.com/codergautam/worldguessr)
- [Issues](https://github.com/codergautam/worldguessr/issues)
- [Project website](https://worldguessr.com)
- [README](https://github.com/codergautam/worldguessr/blob/master/README.md)
- [Releases](https://github.com/codergautam/worldguessr/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/codergautam-worldguessr
