Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
Live runway-in-use at Ben Gurion Airport, and the best spotting location for it right now.
LifestyleJavaScript · HTMLGitHub @ronnyilOnlineOwner 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
Spotting Israel
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A single-purpose site for plane spotters at Ben Gurion Airport (TLV/LLBG): it works
out which runways are actually being used for landings and takeoffs at this moment,
and tells you where to stand.
No build step, no backend. Open index.html or serve the folder statically.
No free source publishes TLV's runway configuration — ATIS on 132.500 is voice only —
so the app derives it from live traffic.
Every 20 seconds it pulls all aircraft within 20 nm of the field, and for each one
checks four things against each of the six runway ends:Read the full README ↗A single-purpose site for plane spotters at Ben Gurion Airport (TLV/LLBG): it works out which runways are actually being used for landings and takeoffs at this moment, and tells you where to stand.
No build step, no backend. Open index.html or serve the folder statically.
No free source publishes TLV's runway configuration — ATIS on 132.500 is voice only — so the app derives it from live traffic.
Every 20 seconds it pulls all aircraft within 20 nm of the field, and for each one checks four things against each of the six runway ends:
data/airport.json carries the local magnetic variation)Low, aligned, on the approach side, descending → landing on that runway. Climbing away past the far end → departing from it. High overflights and taxiing aircraft drop out.
Each aircraft then casts one vote, so a single aircraft in a hold cannot outvote a stream of real traffic. Votes decay with an 8-minute half-life and expire after 20 minutes, which is what lets the reading follow a genuine configuration change instead of averaging across it. Confidence combines how dominant the winning runway is against how much traffic we have actually seen — thin traffic reads as low confidence rather than as a confident guess.
Fallback. When traffic is too thin to read (quiet hours, or every source down), the
app falls back to the published time-of-day pattern and labels it predicted from time of day in amber. It never presents a guess as an observation.
The original idea was to read runway usage off FR24. Two problems:
So the app reads the same underlying ADS-B signal directly from community networks that are keyless and free for non-commercial use, tried in order so one being down is not an outage:
adsb.lol → adsb.fi → airplanes.live
That order is empirical: a CI run against the deployed proxy showed airplanes.live failing and adsb.lol answering, so airplanes.live appears to refuse requests from Cloudflare's datacenter IPs and is tried last.
None of these networks sends an Access-Control-Allow-Origin header, so a browser
will not let the page read their responses, however reachable they are. This was
confirmed from a real phone against the deployed site: all six endpoints answered a
no-cors probe in 45-128 ms while every normal request failed - reachable, but blocked
by policy. No client-side change can work around that.
api/adsb.js is a Vercel serverless function that sits in front of them, adds the CORS
header, and lets Vercel's CDN cache each distinct query. The cache matters beyond
latency: s-maxage=45 means a hundred spotters using the site produce one upstream
request every 45 seconds rather than a hundred polls, which is what these volunteer-run
networks ask for. stale-while-revalidate=900 keeps the last good response serving for
fifteen minutes if the upstreams stop answering.
Requests are clamped to a bounding box around Israel and to 50 nm so it cannot be used as a general-purpose ADS-B proxy.
Not the Edge runtime. Vercel's Edge Functions run on Cloudflare's network, and these upstreams refuse it. The first version of this proxy was a Cloudflare Worker, and it got:
airplanes.live: HTTP 403 adsb.lol: HTTP 429 adsb.fi: HTTP 403
A CI step then called the same endpoints from a GitHub runner and got 200 from both
adsb.lol and adsb.fi seconds later. So the refusal is aimed at Cloudflare's shared
egress IPs, not at datacenter traffic generally - Workers share those IPs across
thousands of customers, and the rate limit applies to a pool this site cannot influence.
The Node runtime runs on AWS and is not caught by it.
airplanes.live returns 403 from everywhere with "Please contact us at [email protected]", which is an access request rather than a block. It stays last
in the upstream list until that access is granted.
worker/adsb-proxy.js is kept as a second deployment target. The block is on
Cloudflare's IP reputation rather than on anything the code does, so it may become
usable again. Both import their shared logic from shared/adsb-core.js, so the upstream
list and validation cannot drift apart.
Free, no card, a couple of minutes.
api/adsb.js as a serverless function on its own.data/config.json as adsbProxy - that is the
deployment URL with /api/adsb on the end, e.g.
https://spotil.vercel.app/api/adsb. The client appends only the query string, so
the same field works for either proxy without host-specific path building.Check it with https://<your-site>/debug.html, which tests the configured proxy
alongside the direct endpoints.
Leaving adsbProxy empty is valid - the site then falls back to the time-of-day
prediction and says so, rather than breaking.
data/spots.json is the local knowledge, and it is the part most worth improving.
The primary source is Moti Kaplan's post in the Facebook group חובבי הספוטינג בישראל (30 Aug 2025), which pairs spots to runways and to time of day:
| When | Landing | Spot |
|---|---|---|
| Morning | 12 (west→east) | Arcafe Or Yehuda, in the car park |
| Late morning–afternoon | 21 (from the north-east) | Bnei Atarot junction / Big Yehud |
| Evening | 30 (from the east) | Airport City / Dor Alon TLV |
| Most of the time | takeoffs from 26 | Western operations area, the old "Bama" spot |
Runway 08 (the reciprocal of 26, used on easterly winds and often at night) and the Route 461 / Hatayasim option come from public spotting guides and are marked as such.
Coordinates are best-effort estimates and every spot carrying one is flagged
coordsApproximate: true. The Navigate buttons therefore search by place name, not
by coordinate, so they route correctly even where a pin is off by a few hundred metres.
Fixing a pin is a one-line edit to data/spots.json — the map and the distance readout
pick it up on reload.
Runways with no spot on file yet are listed under uncovered in the same file.
index.html markup and page shell
assets/style.css styling, light and dark
js/geo.js bearings, distances, cross/along-track maths
js/sun.js solar position and light quality
js/adsb.js live aircraft, via the proxy with direct fallback
js/runway.js runway-in-use detection and vote tracking
js/recommend.js runway → ranked spots
js/i18n.js EN/HE strings
js/app.js UI wiring
data/airport.json runway geometry, magnetic variation, time-of-day pattern
data/spots.json spotting locations
data/config.json proxy URL
debug.html data source diagnostics
shared/adsb-core.js upstream list, validation and fetching, shared by both proxies
api/adsb.js the Vercel proxy (the live one)
worker/ the Cloudflare Worker proxy (blocked by upstreams; kept as a spare)
python3 -m http.server 8777 # then open http://localhost:8777
The modules are plain ES modules, so it must be served over HTTP, not opened as a
file:// URL.