Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
Find fishing boats in a protected Danish sea using satellite radar and AIS data
AI summary · README
GamesTypeScript · PythonGitHub @wyren75OnlineOwner 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
Loading the full README. The saved excerpt is shown below.
A game about finding fishing boats in a protected sea — built by crossing satellite radar, official AIS and Global Fishing Watch over one Natura 2000 site in Denmark.
I built this for two reasons: to get my hands on geospatial data, and to learn how to build software by directing an AI rather than typing every line. The brief I set myself was simple to state and hard to do — pull meaningful information out of data sources that were never designed to work together.Read the full README ↗A game about finding fishing boats in a protected sea — built by crossing satellite radar, official AIS and Global Fishing Watch over one Natura 2000 site in Denmark.
Live: herring-eight.vercel.app
I built this for two reasons: to get my hands on geospatial data, and to learn how to build software by directing an AI rather than typing every line. The brief I set myself was simple to state and hard to do — pull meaningful information out of data sources that were never designed to work together.
The use case came from two things I care about. I'm fascinated by what the new generation of space companies makes possible, and I care about animal protection. Fisheries control is exactly where those two meet.
I also wanted a project I could defend in an interview: not a tutorial, but a problem I chose, measured, and got wrong a few times on the way.
Hirsholmene is a scatter of islands off Frederikshavn, on Denmark's Kattegat coast — a seal and seabird reserve, and a Natura 2000 site protected under European law. It is also trawled. That isn't a secret: the designation protects habitats and species, but fishing is regulated separately, and for most of Europe's marine sites nobody has ever restricted it. Campaigners call these paper parks.
The app puts you in front of a radar image of that site at a precise second and asks you to find the boats. Click a bright dot, and three records that were never designed to meet — Copernicus Sentinel-1 radar, the Danish Maritime Authority's official AIS log, and Global Fishing Watch's classified fishing events — tell you which vessel it was, what it was doing, and whether anyone had noticed. The app walks you through it on first visit.
A second tab, Observation, asks the bigger question: how often do satellites actually look at a protected sea? For Hirsholmene, every 1.7 days. For a UNESCO World Heritage site in West Africa, every 12.
This project is inspired by what Global Fishing Watch already does very well. There is no new concept here. What I've done is zoom in very precisely on one site, add a data source they don't use — the Danish Maritime Authority's shore-based AIS, which is denser than satellite AIS — and present it my own way, with an educational bent.
The fishing shown here is, as far as public records indicate, legal. Nothing in this project accuses any vessel or person of anything. Vessel names and identifiers are what the vessels themselves broadcast by radio, and what the Danish authorities and Global Fishing Watch publish openly. Its purpose is entirely educational.
Every number below was measured with the scripts in data/.
Browser (React + MapLibre)
├── Supabase read-only, anon key all AIS, GFW, scene and pass data
├── Sentinel Hub WMS Copernicus Data Space radar and optical tiles, pinned to a 2-minute window
└── OpenFreeMap vector tiles coastlines
There is no server. Every byte the app reads was produced offline by the ingestion scripts and loaded into Supabase. The browser does one distance calculation per click against ~100 precomputed rows. Everything hard — filtering 2.9 GB of daily AIS, interpolating each vessel to the exact acquisition second, verifying that a satellite footprint actually covers the site — happens once, in Python, where it can be tested.
The one detail that would silently break everything if missed: the radar tile request uses a two-minute window around the acquisition, not a date. Over Denmark there are often two passes a day, twelve hours apart; asking for a date returns the wrong one half the time, and nothing on screen would reveal it.
| Frontend | Vite · React · TypeScript · MapLibre GL · Recharts |
| Data | Supabase (Postgres), read-only from the browser |
| Imagery | Sentinel Hub OGC WMS on the Copernicus Data Space Ecosystem |
| Ingestion | Python 3, requests, standard library — no geospatial dependencies |
| Hosting | Vercel |
data/ ingestion scripts and the CSVs they produced
ingest.py Sentinel-1 scenes + Danish AIS → snapshots at the acquisition instant
ingest_gfw.py Global Fishing Watch events, tested against the site polygon
ingest_passes.py satellite pass counts, footprint-verified, for both sites
rank_sites.py how the site was chosen: fishing vessels at trawling speed, per polygon
out/ the data the app reads, with Supabase schemas
public/ site boundary (EEA Natura 2000), icons
src/ the application
SPEC.md the specification the app was built from, milestone by milestone
DECISIONS.md every significant decision, including the ones rejected and why
CLAUDE.md the working rules given to the AI agent
Two tags mark the history: v0-mvp is the first complete version, with every control in one sidebar; v1 is after a live review reorganised the interface — setup in the top bar, layers and legend on the map, the verdict beside the click. Both versions remain deployable.
| Source | What | Licence |
|---|---|---|
| Copernicus Sentinel-1 and Sentinel-2 | Radar and optical imagery | Free and open; contains modified Copernicus Sentinel data |
| Danish Maritime Authority | Historical AIS, daily files | Open data |
| Global Fishing Watch | Apparent-fishing events, vessel identity | Free for non-commercial use, attribution required |
| European Environment Agency | Natura 2000 site boundaries | Open data |
| OpenFreeMap · OpenMapTiles · OpenStreetMap contributors | Basemap | Open |
git clone https://github.com/wyren75/danish-herring
cd danish-herring
npm install
cp .env.example .env # fill in the five values — see SPEC.md section 6
npm run dev
You'll need a free Supabase project loaded with the CSVs in data/out/ (schemas included), and a free Sentinel Hub configuration on the Copernicus Data Space Ecosystem. SPEC.md section 6 has the exact steps. The ingestion scripts are documented in their headers and need only requests.
Phase 2 — automatic detection. Replace the human click with a brightness-threshold detector over the radar tiles, then score it against Global Fishing Watch's own SAR detections for the same scenes: precision and recall on a live slider. That is where a backend returns, for raw pixel access. Everything downstream of the click is already built to take it.
Other sites. The ingestion is parameterised by a bounding box and a Natura 2000 code. Havet omkring Nordre Rønner, next door, was seven fishing vessels of eight on one of the sampled days.
This project was built by directing an AI coding agent from a written specification, one milestone at a time, with a test to pass before each commit. I wrote the brief, chose the site, ran every measurement, reviewed every milestone live, and made every decision in DECISIONS.md — including the ones where I was wrong first. The agent wrote most of the code and, when it found problems in my data, said so. That was the point: to learn whether I could steer a build I couldn't have typed myself, and defend the result.
Mathieu Fresquet · [email protected]
Questions, corrections and better ideas are all welcome.