# Project skyscraper / no man's sky arg

**URL:** <https://forums.atlas-65.com/t/project-skyscraper-no-mans-sky-arg/9095>\
**Category:** Project Skyscraper\
**Created:** [May 15, 2026, 3:10pm UTC](https://forums.atlas-65.com/t/project-skyscraper-no-mans-sky-arg/9095 "2026-05-15T15:10:02Z")\
**Posts on this page:** 1\
**Showing post:** 2379

<div class="post-metadata">

**Author:** ![vector\_cmdr](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.atlas-65.com/vector_cmdr/32/70640_2.png) [@vector\_cmdr](https://forums.atlas-65.com/u/vector_cmdr)\
**Post date:** [May 29, 2026, 3:50pm UTC](https://forums.atlas-65.com/t/project-skyscraper-no-mans-sky-arg/9095/2379 "2026-05-29T15:50:01Z")

</div>

> **Code with comments (fold it out):**
>
> ```js
> const canvas = document.getElementById('meshCanvas');
> const ctx = canvas.getContext('2d', { alpha: true });
> 
> let width, height, particles = [];
> let mouse = { x: 0, y: 0, active: false };
> 
> // --- Particle: a single node in the network ---
> class Particle {
> constructor() {
> // Spawn at a random angle around screen center, at radius 160-176px
> const cx = width / 2;
> const cy = height / 2;
> const angle = Math.random() * Math.PI * 2;
> const distance = 160 + Math.random() * 16;
> 
> this.baseX = cx + Math.cos(angle) * distance;
> this.baseY = cy + Math.sin(angle) * distance;
> this.x = this.baseX;
> this.y = this.baseY;
> this.size = Math.random() * 2.6 + 1.5; // radius: 1.5–4.1px
> this.angle = Math.random() * Math.PI * 2; // starting orbit phase
> this.orbitSpeed = 0.06 + Math.random() * 0.016; // orbit speed, slightly randomized
> }
> 
> update() {
> // Orbit: rotate around base position (16px wobble)
> this.angle += this.orbitSpeed;
> let tx = this.baseX + Math.cos(this.angle) * 16;
> let ty = this.baseY + Math.sin(this.angle) * 16;
> 
> // Mouse repulsion: if cursor is within 280px, push particle away
> if (mouse.active) {
> const dx = mouse.x - this.x;
> const dy = mouse.y - this.y;
> const dist = Math.hypot(dx, dy);
> if (dist < 280) {
> const force = (1 - dist / 280) * 0.42; // stronger force when closer
> tx += dx * force;
> ty += dy * force;
> }
> }
> 
> // Smooth interpolation (90% previous + 10% target) for laggy movement
> this.x = this.x * 0.9 + tx * 0.1;
> this.y = this.y * 0.9 + ty * 0.1;
> }
> 
> draw() {
> // Intensity: 1 when cursor is on the node, 0 beyond 260px
> const dist = Math.hypot(this.x - mouse.x, this.y - mouse.y);
> const intensity = Math.max(0, 1 - dist / 260);
> 
> // Red glow when mouse is close, cyan glow otherwise
> if (intensity > 0.25) {
> ctx.fillStyle = '#ff2d2d';
> ctx.shadowBlur = 20;
> ctx.shadowColor = '#ff6666';
> } else {
> ctx.fillStyle = '#00ddff';
> ctx.shadowBlur = 8;
> ctx.shadowColor = '#00ddff';
> }
> 
> ctx.beginPath();
> ctx.arc(this.x, this.y, this.size + intensity * 3.2, 0, Math.PI * 2);
> ctx.fill();
> }
> }
> 
> // --- Connection lines between nearby particles ---
> function connectParticles() {
> ctx.lineWidth = 0.7;
> for (let i = 0; i < particles.length; i++) {
> for (let j = i + 1; j < particles.length; j++) {
> const d = Math.hypot(particles[i].x - particles[j].x, particles[i].y - particles[j].y);
> if (d < 170) {
> // Opacity fades from 1 (touching) to 0 (at 170px apart)
> ctx.strokeStyle = `rgba(0, 221, 255, ${1 - d / 170})`;
> ctx.beginPath();
> ctx.moveTo(particles[i].x, particles[i].y);
> ctx.lineTo(particles[j].x, particles[j].y);
> ctx.stroke();
> }
> }
> }
> }
> 
> // --- Animation loop ---
> function animate() {
> ctx.clearRect(0, 0, width, height);
> particles.forEach(p => { p.update(); p.draw(); });
> connectParticles();
> requestAnimationFrame(animate); // runs at ~60fps
> }
> 
> // --- Mouse tracking ---
> window.addEventListener('mousemove', (e) => {
> mouse.x = e.clientX;
> mouse.y = e.clientY;
> mouse.active = true;
> });
> window.addEventListener('mouseleave', () => mouse.active = false);
> 
> // --- Initialization ---
> function init() {
> width = canvas.width = window.innerWidth;
> height = canvas.height = window.innerHeight;
> particles = [];
> 
> // Particle count scales with day of month: max ~48 on day 28, min 3
> const day = new Date().getDate();
> const count = Math.max(3, Math.round(day * (48 / 28)));
> for (let i = 0; i < count; i++) particles.push(new Particle());
> 
> animate();
> }
> 
> window.addEventListener('resize', init);
> init();
> 
> // --- Live Connections polling ---
> // Every 15 seconds, re-fetch the page and scrape the "Live Connections"
> // counter from the server-rendered HTML. This is how the number updates
> // without a full page reload — no WebSocket, no SSE.
> setInterval(() => {
> const container = document.getElementById('live-visitors');
> if (!container) return;
> fetch(window.location.href)
> .then(r => r.text())
> .then(html => {
> const temp = document.createElement('div');
> temp.innerHTML = html;
> const newContent = temp.querySelector('#live-visitors');
> if (newContent) container.innerHTML = newContent.innerHTML;
> });
> }, 15000);
> 
> ```

TL;DR:

- Nodes orbit in a loose ring around screen center, connected by cyan lines when close.
- Moving your mouse repels nearby nodes and turns them red.
- Number of nodes depends on the current day of the month.
- “Live Connections” counter is just fetch() polling every 15s - nothing real-time.
- When you grab a new copy via load and a new execution runs, the endpoint returns a value.
- It runs at z-index: -1 behind all page content - purely decorative.

Also: [Project Skyscraper - Frequently Asked Questions](https://forums.atlas-65.com/t/project-skyscraper-frequently-asked-questions/9393)  
for reference.

---

_[View the full topic](https://forums.atlas-65.com/t/project-skyscraper-no-mans-sky-arg/9095)._
