OhMyGame
OhMyGame is the open-source studio for making games with AI. Design, build, playtest, and publish games in one app, with your own model keys.
A browser maritime game: sail a 3D ocean, build a trading empire, and explore documented maritime history.
GamesTypeScript · PLpgSQLGitHub @faizismail-FMOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A standalone browser game that combines a 3D ship simulator, a maritime empire builder, historical trade campaigns, fleet management and a source-backed historical encyclopedia. Built with Next.js (App Router), TypeScript, React Three Fiber, Tailwind CSS and Zustand. It runs entirely in the browser with no paid APIs; Supabase persistence is optional.
The full design brief is in docs/MASTER_PROMPT.md. This repository implements the first playable release described in its Part 16: six complete scenarios, a working 3D ocean with controllable vessels, a global port map, contracts and economy, ship purchasing, port development, technology, diplomacy, missions, save/restore, and the encyclopedia. A player guide (/guide, also opened with ? in the sailing view and from the ? buttons on each screen) explains the core loop, controls, time scale, trade, fleet and ports.
| Scenario | Period | You play | Highlights |
|---|---|---|---|
| Merchants of Siraf | 850–900 CE, Abbasid Gulf | A fictional merchant house at Siraf | 11 period ports (Siraf, Basra, Suhar, Aden, Kollam, Mantai, Kalah, Khanfu…), sewn-plank ships, monsoon winds and provisions, the Zanj revolt and the 879 sack of Guangzhou as dated events |
| Sultans of Kilwa | 1330–1380 CE, Swahili coast | A fictional merchant house at Kilwa | 9 ports from Sofala to Hormuz and Cambay, sewn ships and the mtepe, Sofala gold for Indian cloth, Ibn Battuta's visit, the Gujarat revolts and the Black Death at Aden as dated events |
| Fleets of the Western Ocean | 1405–1435 CE, early Ming China | A fictional Fujian shipping house in the wake of the treasure fleets | 19 ports from the Yangtze mouth, Ryukyu and Hakata across the South China Sea to Champa, Siam, Palembang, Melaka, Ceylon, Bengal, Calicut, Hormuz, Aden and the Swahili coast, sand ships, Fujian junks and the treasure ship (drawn at Church's 62 m, not the Ming History's 137 m), the sea ban and the tribute tallies, Chen Zuyi 1407, Galle 1409, Parameswara at Nanjing 1411, Hormuz 1414, the halt of 1424 and the last fleet 1431–1433 as dated events |
| Galleys of Venice | 1423–1463 CE, Republic of Venice | A fictional patrician house at Venice | 12 ports from Sluys to Alexandria, round ships and state-auctioned great galleys, Mediterranean winds, Thessalonica, Barsbay's pepper monopoly and the fall of Constantinople; campaign ends at the Ottoman war of 1463 |
| Emporium of the Straits | 1460–1511 CE, Melaka Sultanate | A fictional trading house at Melaka | 15 ports from Cambay to Banda, jong and hybrid junks, Ming tribute access, campaign ends at the Portuguese capture of 1511 |
| Carreira da Índia | 1505–1550 CE, Portuguese Cape route | A fictional Lisbon merchant house under the Crown's licence | 14 ports from Antwerp and Mina round the Cape to Cochin, Goa, Hormuz and Malacca, caravels, naus and galleons, the Crown's pepper monopoly, Diu 1509, Goa 1510, Malacca 1511, Hormuz 1515 and the Ottoman siege of Diu 1538 as dated events |
| The Company's Monopoly | 1602–1652 CE, Dutch East India Company | A fictional Amsterdam merchant house under the Company's charter | 16 ports from Amsterdam round the Cape on Brouwer's route to Bantam, Batavia, Amboina, Banda, Hirado, Coromandel, Surat, Mocha, Gamron and Formosa, jachts, fluyts and retourschepen, Japanese silver and Coromandel cloth, Amboina 1605, Batavia 1619, Banda 1621, Malacca 1641, Dejima 1641 and the Cape 1652 as dated events |
| Modern Shipping Empire | 2024, open-ended | A fictional container line at Port Klang | 20 real container ports, feeder to ULCV classes, fuel, terminal fees, the Port Klang → Singapore tutorial |
Every historical record carries an evidence rating (established / interpretation / reconstruction / uncertain / fictional) and its sources. See docs/RESEARCH_NOTES.md for what still needs verification.
npm install
npm run dev # http://localhost:3000
Other scripts:
npm run typecheck # tsc --noEmit (strict)
npm run lint # eslint
npm run test # vitest: geography, kinematics, autopilot, economy, contracts, data integrity, tutorial run
npm run build # production build
npm run check # all of the above
No environment variables are needed to play. Saves are kept in the browser (localStorage) and can be exported as JSON from Settings.
Automated: with a Supabase personal access token exported as SUPABASE_ACCESS_TOKEN, run
node scripts/setup-supabase.mjs
It creates (or reuses) a project named ocean-empire-3d, applies the migration, enables anonymous sign-ins and prints the two public variables. If VERCEL_TOKEN, VERCEL_PROJECT and VERCEL_TEAM_SLUG are also set it writes them to the Vercel project and redeploys production. The script has been run against a live Supabase account to provision the production project; see its header for the API calls it makes.
Manual:
supabase/migrations/0001_init.sql in the SQL editor..env.example to .env.local and fill in NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY (for Vercel, add the same two variables in the project settings and redeploy).Only the public anon key is used client-side; rows are protected by row-level security. Never put a service-role key in a NEXT_PUBLIC_ variable.
The world map and the encyclopedia's Historical maps page draw dated layers (src/data/historical/territories.ts, helpers in src/lib/map-layers.ts): territorial control as coarse, approximate outlines with a control type (direct rule, suzerainty, sphere, rebel-held), a period, an evidence rating and sources, read at the current game year so Zanj Iraq, Thessalonica, Constantinople, Cyprus and the Morea change during their campaigns; inland capitals and settlements for that year; port ownership by polity; documented routes; the player's own trade network and influence; and present-day borders, which historical campaigns draw only on request and mark as reference only. Hovering an outline shows its period and source ids; the maps page has a year slider, jump buttons for the years in which control changes, and a panel listing every extent with its sources. Polities without a dated extent fall back to their undated approximate region.
Sovereign territory is never for sale. Historical scenarios define regions (groupings of their ports, rated as reconstructions) and a territorial rule with its historical basis (regions and territory_rules in the scenario data; engine in src/engine/territory.ts). Every port carries the house's commercial influence (0 to 100), which relaxes daily toward a target set by the house's share of the port's traffic, its facilities, its holdings and rival development, and is always 100 at home. Where the rules allow, the house can found a trading settlement at a port it has called at (influence 25, relations 50 with the ruler): building and ship purchase rights, an extra weekly contract, agreement-level fees and a daily upkeep. Kilwa and Melaka also allow a settled port with influence 70, the larger part of its traffic and the ruler's favour to be brought under the house's protection: lower fees, two extra contracts and customs duties at half the home rate, at the cost of relations with its ruler; great cities are never available. The Empire screen summarises influence by region, the world map draws influence rings and holding markers, and the port panel offers the actions with the reason when one is unavailable. Holdings are gameplay inventions and the encyclopedia keeps the documented rulers.
A historical house can be created under sandbox rules (empire.rules === "sandbox"). The documented timeline is unchanged, but at each turning point (a dated event with a gameplay effect) the simulation pauses and the player chooses what the game does: the documented consequence, nothing (averted), a softened version (closures become restrictions, multipliers move half as far) or the reverse. Decisions can be set in advance from the History screen. Rivals react to what actually happened in the game. Every departure from the record is logged in state.alternate_history.divergences, shown with a fiction label on the History screen, in the empire dashboard and in the top bar, and never touches the reference database (src/engine/alternate.ts). A sandbox campaign continues past its historical end unless the player lets the ending event happen.
With Supabase configured, every browser first gets an anonymous Supabase identity so its saves can be mirrored. To keep saves across devices, players sign in with their email from the main menu or Settings → Account: the game emails a sign-in link (no password), and opening that link on a device signs it in. An anonymous identity is upgraded in place when possible, so existing saves keep their ids; otherwise the device's local saves are uploaded to the account on sign-in (re-identified if their ids already belong to an old anonymous identity). The Continue list merges local and account saves and loads whichever copy is newer.
Operational notes:
https://ocean-empire-3d.vercel.app/**) for the links to land on the game; scripts/setup-supabase.mjs does not set these, see Authentication → URL Configuration.Encyclopedia entry pages can carry an "Ask about this entry" box. It is served by src/app/api/history-assistant/route.ts, which grounds Claude (claude-opus-5-5) in the entry's own record, closely related records and the bibliography behind them (src/lib/history-grounding.ts) and instructs it to answer only from that material, to say when the record does not cover a question, to label interpretation, reconstruction and fiction, and to cite the bracketed source ids. Gameplay tuning values are stripped from the material so they are never presented as history. The player's question and the entry material are sent to Anthropic's API; nothing else about the player is.
Set ANTHROPIC_API_KEY in the server environment (for Vercel: Project → Settings → Environment Variables, then redeploy) to enable it. Without the key the route answers that the assistant is not enabled and the page says so; the game and the encyclopedia need no API key, as the brief requires.
Import the repository in Vercel. The defaults (framework: Next.js, build next build) work as-is. Add the two Supabase variables in the project settings only if you want remote saves. The project does not depend on any other deployment, API, domain or repository.
Each port carries three indicators (src/engine/demographics.ts): population in abstract thousands seeded from the port's size and period, prosperity (0–100) pulled daily toward a target set by recent ship calls, market turnover, facilities, fees and access, and public order, which recovers slowly and collapses when an event closes the port. They feed back into play: prosperity and population scale the market's weekly stock and the number and value of contracts, disorder raises port charges, facilities above level 2 need a prosperous workforce, and the home port's figures shape the empire's stability and daily duties. Older saves seed the indicators from the port record on first load.
Each scenario defines three AI trading powers (rivals in the scenario data; engine in src/engine/rivals.ts). They run fleets on the same sea lanes and the same contract board as the player: a docked rival ship takes one of the contracts on offer at its port or sails on its own account, arrival pays it and records a port call, and with money in hand it buys ships and builds cargo handling at its busiest port. Traffic share per port is derived from those calls; a rival above half the traffic raises the player's terminal charges there by 10%. Relations with the player drift toward neutral, fall when the player accepts contracts at the rival's home port, and can be raised with courtesies or a partnership. Own-account voyages buy and sell real goods: a rival ship with no contract buys the good with the best live margin within reach and sells it on arrival through the shared market ledger (state.market, per port, good and week), so rival purchases draw down the stock the player sees and rival sales weigh on the price (pricePressure in src/engine/market.ts, bounded to 0.6 to 1.5). Rivals also trade with each other: each posts charters at its home port that any carrier (the player included) may take, with the charterer paying on delivery; calling at another rival's home port pays that rival a handling fee; mutual standing built this way forms alliances at 70 (no bidding at each other's home ports, preference for each other's charters) that break below 40. Rivals react to historical events through the events' own effects: ships bound for a port an event closes or makes expensive divert to the nearest open port, a rival based there loses money, price booms draw rivals to the goods' suppliers, and a relations change for a rival's polity moves its standing with the player. Rivals appear on the world map, in the sailing view when nearby, and on the Rivals screen, which also lists each rival's responses to events. All rivals are fictional, and the engine is deterministic from the save's seed. Saves from before this feature load with no rivals.
All sound is synthesised in the browser with the Web Audio API; no audio files are shipped or downloaded. In the sailing view a looping noise source is shaped into sea (low-passed, with a slow swell) and wind (band-passed, its level and pitch following the simulated wind), sailing ships creak under way and motor ships hum with the throttle. A generative music bed plays a drone and a sparse random-walk melody in a palette chosen for the campaign (src/lib/audio/music.ts: a hijaz-flavoured plucked tone for Siraf, a pentatonic zither for the Ming fleets, a bowed Dorian line for Venice, a slendro-like metallic tuning for Melaka, harpsichord for the Dutch Company, synthesiser pads for the modern line). The palettes set a mood and do not claim to reproduce period music. Music also plays quietly on the main menu and management screens; the encyclopedia is silent. Browsers only allow sound after a user gesture, so it starts on the first click or key; the speaker button in the sailing bar and on the main menu mutes it, and Settings has separate levels for master, music and ambience.
| Key | Action |
|---|---|
| W / S | Increase / decrease throttle or sail |
| A / D | Steer to port / starboard (hold) |
| Space | Pause / resume |
| C | Cycle camera (chase, free, overhead) |
| + / − | Zoom, R to reset |
| Esc | Game menu |
All controls are also available as on-screen buttons. Keys are ignored while typing in a form field. Any manual input disengages the autopilot.
src/data/sealanes.ts) so ships round peninsulas instead of crossing land; canal nodes are disabled before their opening years.Daily upkeep is charged per ship; a home-port customs house produces income. Prices drift deterministically per week from a seed, so a save always sees the same markets.
src/data Immutable reference database (Zod-validated): ports, ships, goods, technologies,
events, figures, routes, civilizations, sources, scenarios, sea lanes, infrastructure
src/engine Pure simulation rules: geo, kinematics, wind, autopilot, routing, economy, markets,
contracts, port development, technology, objectives, events, game tick, save validation
src/store Zustand stores wrapping the engine (game, settings)
src/persistence Storage adapters: localStorage (default) + optional Supabase mirror
src/components UI primitives, game screens, the d3-geo world map, the R3F sailing scene and HUD
src/app Next.js routes: menu, new game, /play/* screens, /encyclopedia/*
supabase Optional database schema with RLS
tests Vitest suites (89 tests)
docs Master design prompt and research notes
Design rules kept throughout:
ssr: false), with a WebGL fallback.Quality presets (low / medium / high) control device pixel ratio, shadows and ocean tessellation (10 m, 5 m or 3.5 m cells on the 1.4 km displaced sheet around the ship; a flat sheet carries the water to the fog). The ocean is a sum of eight swell components that displace the mesh plus three ripple components that only perturb per-pixel normals. Every component keeps its own phase: each frame the phases advance by real time and by the difference between the ship's real-speed movement and its scene movement, so the water passes the hull at the ship's actual speed at any time scale and the wake, bobbing and glitter never degrade into noise at 60× or 3600×. The wake is stored relative to the ship and recedes at real speed for the same reason. The 3D scene reads game state through a per-frame shared record rather than React re-renders; the HUD updates at about 8 Hz. The simulation integrates ship movement in substeps of at most 120 simulated seconds so high time acceleration does not skip waypoints. Frame rates depend on hardware and have not been benchmarked; no specific figure is claimed.
npm run check (typecheck, lint, 89 unit/integration tests, production build) passes. The production build was also driven end to end in headless Chromium with Playwright: new game → accept the Port Klang→Singapore contract → depart → autopilot voyage at accelerated time → automatic docking, payment and voyage report → save restored after reload, plus the Siraf, Kilwa, Ming, Venice, Melaka, Carreira and Dutch Company sailing scenes, diplomacy, history and encyclopedia pages.
This is the first vertical slice. Seven historical campaigns cover seven civilization families from the brief (Abbasid-era Gulf trade, the Swahili city-states, Chinese maritime commerce under the early Ming, Venetian commerce, the Melaka Sultanate, Portuguese expansion and Dutch commerce); the other families it lists (Srivijaya, Chola, Song China, British and the ancient Mediterranean) are present only as polities and ports inside these campaigns, not as playable campaigns of their own. AI trading powers, the population and prosperity indicators and the optional AI history assistant are in place. Supabase sync and email sign-in run against a live project in production. Sign-in emails use Supabase's built-in mailer, which is limited to a few per hour until custom SMTP is configured (see "Accounts and cross-device saves"). The bibliography has been checked against catalogues and reviews, and the remaining open questions in docs/RESEARCH_NOTES.md (Kalah's site, Daybul's identification, Aden in 850, the Kilwa regnal dates, the fourteenth-century harbour of Zanzibar) are open in the scholarship itself, and rendering has only been checked on a software renderer and one laptop GPU, with no frame-rate benchmarks.