A live night sky where every Instagram follower is a real star. The moon shows today's actual lunar phase. Lose a follower and shooting stars streak across the sky.
Live at: https://enginyears.github.io/nightsky/
| Feature | Detail |
|---|---|
| π Real moon | Mathematically accurate phase, mare regions, craters, limb darkening, earthshine on new moon |
| β Follower stars | Exactly N stars = your follower count, stable seeded positions |
| π¨ Spectral colours | O/B blue-white, A white, F/G yellow, K orange, M red β weighted by real stellar frequency |
| π± Star cursor | OS cursor hidden, replaced with a twinkling 8-spike diffraction star drawn on canvas |
| π Mouse attraction | Follower stars drift toward the cursor when nearby, snap back instantly when it leaves |
| π Parallax | Background stars shift on mouse movement across three depth layers |
| π Milky Way | Seeded diagonal star-dust band |
| πΏ Aurora | Wavy green/teal/violet bands shimmer at the horizon |
| π Shooting stars | Auto every 8β26 s. Lose followers? One streak per lost follower fires immediately |
| β¨ Gain flash | Gold +N β¦ floats up when your count increases |
| π Last synced | Top-right pill shows the exact date and time of the last successful data sync in IST |
nightsky/
βββ index.html # The entire webpage (self-contained, no dependencies)
βββ data.json # Written by fetch.py, read by the webpage
βββ fetch.py # Fetches follower count from Instagram
βββ requirements.txt # Python dependencies (instaloader)
βββ README.md
βββ .github/
βββ workflows/
βββ sync.yml # GitHub Actions β runs fetch.py every 30 min
Your home machine (residential IP)
β
β self-hosted GitHub Actions runner
β runs fetch.py every 30 minutes
β
βΌ
Instagram API βββΊ data.json βββΊ git commit + push
β
βΌ
GitHub Pages serves
the updated file
β
βΌ
Browser fetches data.json
every 30 min (cache: no-store)
and redraws the star field
Instagram blocks all requests from cloud datacenter IPs (AWS, Azure, Cloudflare). Your local machine has a residential IP that Instagram doesn't block. The self-hosted GitHub Actions runner is just a small background service running on your home machine that lets GitHub trigger jobs on it remotely.
Create a public repository, push all files, then go to Settings β Pages β Source: Deploy from branch β Branch: main / (root). Your page will be live within a minute.
In your GitHub repo go to Settings β Actions β Runners β New self-hosted runner β Linux.
Follow the four commands GitHub shows you. Run them on your home machine (the same machine where python3 fetch.py works locally). After ./run.sh confirms it's connected, install it as a permanent service so it survives reboots:
sudo ./svc.sh install
sudo ./svc.sh startThe runner will now start automatically on boot and sit idle until GitHub triggers it.
Go to Actions β π Sync Galaxy β Run workflow. This runs fetch.py on your home machine, writes data.json with your real follower count, commits it, and pushes. After that it runs automatically every 30 minutes.
To confirm it worked, check that data.json in your repo now has a non-zero follower_count and a recent fetched_at timestamp.
No framework, no build step β index.html is entirely self-contained vanilla JavaScript using the browser's Canvas 2D API.
The moon is drawn once to an offscreen <canvas> element and cached. It is only rebuilt when the lunar phase shifts by more than 0.15% (roughly every few minutes). The terminator (day/night boundary) is approximated with a cubic bezier curve using the formula ex = R Γ cos(2Ο Γ phase) to compute the ellipse semi-axis. Phase 0 = new moon, 0.5 = full moon.
All positions use a seeded pseudo-random number generator (mulberry32). The same seed always produces the same sequence of positions, so the star field looks identical on every page load and every browser. Adding new followers appends new stars without shifting existing ones.
Each follower star carries a displacement offset (dx, dy) that starts at zero. Every frame:
- If the cursor is within
ATTRACT_RADIUSpixels,dx/dyis nudged toward the cursor proportional to proximity (closer = stronger pull) dxanddyare multiplied byDECAY = 0.78β exponential decay that pulls the star back home when the cursor leaves- The star is drawn at
(x + dx, y + dy)
The cursor and attraction calculations use raw mouse coordinates (mx, my). The parallax effect uses smoothed coordinates (smx, smy β lerped 4% per frame) because that lag creates a pleasing elastic feel. Anything the user controls gets raw values; anything that reacts can be smoothed.
data.json is fetched with { cache: 'no-store' } to force a real network request on every refresh, preventing GitHub Pages' CDN from serving a stale copy.
All the interesting constants are at the top of drawLoop in index.html:
| Constant | Default | Effect |
|---|---|---|
ATTRACT_RADIUS |
150 | How close the cursor must be to pull a star (px) |
ATTRACT_FORCE |
0.55 | How eagerly stars accelerate toward the cursor |
DECAY |
0.78 | How fast displacement fades β lower = snappier return |
MAX_DISPLACE |
36 | Maximum drift from home position (px) |
pip3 install instaloader
python3 fetch.py # writes data.json locally
# Must serve over HTTP β fetch() is blocked on file:// origins
python3 -m http.server 8000
# Open http://localhost:8000