Local demo session
DISTROFLUX SESSION LOGIN
Create a development account to persist camera profiles, point assignments, origin/settings, and future geofence profiles to MySQL.
Local mode still works. Login enables server persistence through the /api PHP backend.
DISTROFLUX // UNIFIED DIRECTIVE MESH
Simulation-only proof of concept: Fibonacci sphere distribution, lat/long cardinal mapping, credential-backed sessions, coordinate origin anchoring, point-assigned camera profiles, equal-agent swarm roles, protect-card escorting, and abstract target assessment.
Mesh Topology: FIBONACCI SPHERE
Directive Model: EVERY NODE = MAIN NODE
Active Nodes: 400
Mode: BOOTING
Protect Cards: 0
Sim Targets: 0
Assigned Drones: 0
Last Lat/Lon: AWAITING PING
Profile Origin: NOT SET
Session: LOCAL DEMO
Camera Links: 0
Consensus Reached: 0%
Hardware Density400
Mesh / Coordinates
Simulator Scenarios
Selected Node / Cardinal Translation
Click a drone point on the sphere to inspect its node ID, lat/long, hemisphere, cardinal sector, current role, and assigned entity.
Origin / Coordinate Workbench
Click any mesh node or globe surface point. The simulator will translate it into lat/long, hemisphere, cardinal sector, and Fibonacci slot context.
Origin anchors the future property/swarm profile. Paste decimal coordinates or Google Maps DMS text like 55°32'38.6"N 132°46'18.3"E.
Event Log
SIMULATION LEGEND
Idle mesh node   Protect escort   Scanner/assessment   Target containment   Selected
This build keeps all behavior abstract: inspection, assessment, escort, containment, and simulated resolution.
POINT CAMERA GALLERY
Select a mesh point to view assigned cameras.
Select a camera tile to inspect assignment details, preview, or open a focused inspection view.
CAMERA INSPECTION
Focused view
`); tab.document.close(); log(`[CAM] Focused inspection tab opened for ${cameraId}.`); } function escapeHtml(value) { return String(value ?? "").replace(/[&<>'"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ }[ch])); } function escapeXml(value) { return String(value ?? "").replace(/[&<>'"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ }[ch])); } // ------------------------------------------------------------ // Geometry helpers // ------------------------------------------------------------ function vectorToLatLon(vec) { const v = vec.clone().normalize(); const lat = Math.asin(clamp(v.y, -1, 1)) * (180 / Math.PI); const lon = Math.atan2(v.z, v.x) * (180 / Math.PI); return { lat, lon }; } function latLonToVector(latDeg, lonDeg, radius = sphereRadius) { const lat = THREE.MathUtils.degToRad(latDeg); const lon = THREE.MathUtils.degToRad(lonDeg); const x = Math.cos(lat) * Math.cos(lon); const y = Math.sin(lat); const z = Math.cos(lat) * Math.sin(lon); return new THREE.Vector3(x, y, z).normalize().multiplyScalar(radius); } function getHemisphere(lat, lon) { const ns = lat >= 0 ? "NORTH" : "SOUTH"; const ew = lon >= 0 ? "EAST" : "WEST"; return `${ns} / ${ew}`; } function getCardinalSector(lat, lon) { const absLat = Math.abs(lat); const absLon = Math.abs(lon); if (absLat < 8 && absLon < 8) return "CENTER / EQUATOR-PRIME"; if (absLat >= 72) return lat >= 0 ? "N" : "S"; if (absLat < 12) return lon >= 0 ? "EQUATOR-E" : "EQUATOR-W"; if (absLon < 12) return lat >= 0 ? "N-PRIME" : "S-PRIME"; const ns = lat >= 0 ? "N" : "S"; const ew = lon >= 0 ? "E" : "W"; return `${ns}${ew}`; } function getTangentBasis(normal) { const helper = Math.abs(normal.y) < 0.92 ? new THREE.Vector3(0, 1, 0) : new THREE.Vector3(1, 0, 0); const u = new THREE.Vector3().crossVectors(helper, normal).normalize(); const v = new THREE.Vector3().crossVectors(normal, u).normalize(); return { u, v }; } function makeSurfaceCircle(centerVector, angularRadius, hexColor, opacity) { const normal = centerVector.clone().normalize(); const { u, v } = getTangentBasis(normal); const pts = []; const segments = 120; for (let i = 0; i <= segments; i++) { const a = (i / segments) * Math.PI * 2; const dir = normal.clone().multiplyScalar(Math.cos(angularRadius)) .add(u.clone().multiplyScalar(Math.cos(a) * Math.sin(angularRadius))) .add(v.clone().multiplyScalar(Math.sin(a) * Math.sin(angularRadius))) .normalize() .multiplyScalar(sphereRadius + surfaceLift); pts.push(dir); } const mat = new THREE.LineBasicMaterial({ color: hexColor, transparent: true, opacity, blending: THREE.AdditiveBlending }); return new THREE.LineLoop(new THREE.BufferGeometry().setFromPoints(pts), mat); } function makeTextSprite(text, color = "#f4fdff", width = 1.8, height = 0.55) { const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 160; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.font = "bold 58px monospace"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.shadowColor = color; ctx.shadowBlur = 18; ctx.fillStyle = color; ctx.fillText(text, canvas.width / 2, canvas.height / 2); const texture = new THREE.CanvasTexture(canvas); texture.needsUpdate = true; const mat = new THREE.SpriteMaterial({ map: texture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }); const sprite = new THREE.Sprite(mat); sprite.scale.set(width, height, 1); return sprite; } function makeOriginIdentitySprite(profile) { const canvas = document.createElement('canvas'); canvas.width = 700; canvas.height = 360; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = "rgba(4,10,18,0.86)"; ctx.strokeStyle = "#ffaa00"; ctx.lineWidth = 8; ctx.shadowColor = "#ffaa00"; ctx.shadowBlur = 18; roundRect(ctx, 24, 28, canvas.width - 48, canvas.height - 56, 24); ctx.fill(); ctx.stroke(); ctx.shadowBlur = 12; ctx.fillStyle = "#ffaa00"; ctx.font = "bold 42px monospace"; ctx.textAlign = "center"; ctx.fillText("PROFILE ORIGIN", canvas.width / 2, 82); ctx.shadowBlur = 0; ctx.fillStyle = "#f4fdff"; ctx.font = "bold 32px monospace"; ctx.fillText(`${profile.lat.toFixed(5)}, ${profile.lon.toFixed(5)}`, canvas.width / 2, 150); ctx.fillStyle = "#44ffb3"; ctx.font = "bold 27px monospace"; ctx.fillText(`${profile.cardinal} / FIB-${String(profile.fibonacciIndex).padStart(4, '0')}`, canvas.width / 2, 205); ctx.fillStyle = "#7aa0a4"; ctx.font = "bold 22px monospace"; ctx.fillText(profile.id, canvas.width / 2, 270); const texture = new THREE.CanvasTexture(canvas); texture.needsUpdate = true; return new THREE.Sprite(new THREE.SpriteMaterial({ map: texture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending })); } function roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); } function makeProtectSprite(id) { const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, 512, 512); ctx.strokeStyle = "#44ffb3"; ctx.fillStyle = "#44ffb3"; ctx.lineWidth = 16; ctx.shadowColor = "#44ffb3"; ctx.shadowBlur = 26; // Stick person ctx.beginPath(); ctx.arc(256, 112, 42, 0, Math.PI * 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(256, 154); ctx.lineTo(256, 300); ctx.moveTo(174, 212); ctx.lineTo(338, 212); ctx.moveTo(256, 300); ctx.lineTo(188, 420); ctx.moveTo(256, 300); ctx.lineTo(324, 420); ctx.stroke(); ctx.font = "bold 48px monospace"; ctx.textAlign = "center"; ctx.fillText("PROTECT", 256, 478); ctx.font = "bold 22px monospace"; ctx.fillStyle = "#f4fdff"; ctx.fillText(id, 256, 32); const texture = new THREE.CanvasTexture(canvas); texture.needsUpdate = true; return new THREE.Sprite(new THREE.SpriteMaterial({ map: texture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending })); } function makeTargetSprite(id, level) { const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512; const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, 512, 512); ctx.strokeStyle = level === "proximity" ? "#ffaa00" : "#ff2bd1"; ctx.fillStyle = ctx.strokeStyle; ctx.lineWidth = 14; ctx.shadowColor = ctx.strokeStyle; ctx.shadowBlur = 24; ctx.beginPath(); ctx.arc(256, 256, 112, 0, Math.PI * 2); ctx.moveTo(256, 92); ctx.lineTo(256, 180); ctx.moveTo(256, 332); ctx.lineTo(256, 420); ctx.moveTo(92, 256); ctx.lineTo(180, 256); ctx.moveTo(332, 256); ctx.lineTo(420, 256); ctx.stroke(); ctx.beginPath(); ctx.arc(256, 256, 20, 0, Math.PI * 2); ctx.fill(); ctx.font = "bold 34px monospace"; ctx.textAlign = "center"; ctx.fillText("SIM TARGET", 256, 478); ctx.font = "bold 22px monospace"; ctx.fillStyle = "#f4fdff"; ctx.fillText(id, 256, 38); const texture = new THREE.CanvasTexture(canvas); texture.needsUpdate = true; return new THREE.Sprite(new THREE.SpriteMaterial({ map: texture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending })); } // ------------------------------------------------------------ // Utility // ------------------------------------------------------------ function randomRange(min, max) { return min + Math.random() * (max - min); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function clampInt(value, min, max) { return Math.max(min, Math.min(max, Math.round(value))); } function normalizeLon(lon) { let out = lon; while (out > 180) out -= 360; while (out < -180) out += 360; return out; } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }