-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
357 lines (326 loc) · 18.1 KB
/
index.js
File metadata and controls
357 lines (326 loc) · 18.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
(function() {
// ----- SLIDE / SCROLL BEHAVIOR (enter archive) -----
const enterBtn = document.getElementById('archiveButton');
const targetSection = document.getElementById('nodeMapSection');
function slideToNodeMap() {
if (targetSection) {
targetSection.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest'
});
setTimeout(() => {
const canvasElem = document.getElementById('nodeCanvas');
if (canvasElem) {
canvasElem.style.transition = 'box-shadow 0.2s';
canvasElem.style.boxShadow = '0 0 0 2px #5effbc, 0 0 20px #00c3ff';
setTimeout(() => {
if(canvasElem) canvasElem.style.boxShadow = '';
}, 800);
}
}, 500);
}
}
if (enterBtn) {
enterBtn.addEventListener('click', (e) => {
e.preventDefault();
slideToNodeMap();
});
}
// ----- NODE MAP GRAPHICS (same as before with extra node lore & press handling) -----
const canvas = document.getElementById('nodeCanvas');
let ctx = canvas.getContext('2d');
let width, height;
// Define nodes with rich lore data for each node
const nodes = [
{ id: 0, label: "ORIGIN", type: "core", x: 0.5, y: 0.2, color: "#ffb347", radius: 18,
desc: "Primary Archive Seed",
fullLore: "The internet started with the ARPANET → The first website was https://info.cern.ch/, where researchers developed the first website using protocols and hosted the site publicly. This was the first time the public could use the internet to show their own works. It started off as a way of social expression, which developed into an access to companies all around the world. The more technical side of things that facilitated the initialisation of the internet. We cannot start this journey without this event. The first message sent over the internet was to send a message across the world has actually crashed midway through sending it, for the infamous first message being sent called “LO” to spell “LOGIN” in 1969 at UCLA. " },
{ id: 1, label: "ECHO", type: "link", x: 0.28, y: 0.45, color: "#bc7af9", radius: 14,
desc: "Resonant Memory Loop",
fullLore: "This gave rise to memes, internet pop culture in the early 2000s, since knowing people has never been so easy in human life before. Early modern internet morphed this culture into meme culture, which has a combination of creativity and a specific coded language based on the community it developed from. So what was the first meme ever commercialised? For a lot of Gen-Z, who we consider to be raised on the internet, we can assume that they have probably never seen this meme because it dates all the way back to 1996! The following clip is a representation of what meme culture stemmed from. It’s considered the grandfathers of all memes, considering its age. But how did it not survive in the current day and age? Well, it was a meme that widely surfaced on Geocities, where people could make personal free webpages, and it spread within the community. Then, it was acquired by Yahoo!, and in 2009, it closed forever. This is only one of the many beginning examples of the dead internet theory. That is, this meme of a dancing baby. Yep, that’s right. ",
aditionalimg: ""},
{ id: 2, label: "SPECTRA", type: "data", x: 0.72, y: 0.48, color: "#42e5ff", radius: 15,
desc: "Visual Data Stream",
fullLore: "Social graph and social shifts – The earliest and most popular form of the internet has been MySpace, an online connection forum that most people learn to use as children. This gave form to the social graph and the connections that early Facebook used to engage the idea of digital identities. The earliest and most popular form of this is MySpace! Many millennials grew up on this space, and many records of it still exist! It was the first virtual teenager playground where people could customise their webpages and text each other instantly. It’s the first widely used instant messenger." },
{ id: 3, label: "NEXUS", type: "core", x: 0.5, y: 0.7, color: "#ffb347", radius: 20,
desc: "Central Node — Archive Heart",
fullLore: "Insert Data" },
{ id: 4, label: "FRAGMENT", type: "data", x: 0.18, y: 0.78, color: "#42e5ff", radius: 12,
desc: "Lost Recording Fragment",
fullLore: "The art of the internet is another fascinating thing to preserve. In my opinion, there were many interesting pieces, but the most important one that shows the collaboration of the internet is the Reddit forum for r/place. It was a collaborative pixel board that closed in 2023. Started in the early 2000s when pixel art was taking off, it became a holder of many people’s ideas, philosophies, and inside jokes of many communities. This board changed over the years; there were intergroup wars over who could occupy the most space. However, it all equalised to showcase the effort of people in the past, right up to the present. It was an ever-changing mural." },
{ id: 5, label: "GLYPH", type: "link", x: 0.82, y: 0.75, color: "#bc7af9", radius: 13,
desc: "Quantum Entanglement Link",
fullLore: "Insert Data" },
{ id: 6, label: "MEMORY-7", type: "core", x: 0.35, y: 0.88, color: "#ffb347", radius: 14,
desc: "Ancient Data Vault",
fullLore: "The childhood nostalgia – Everyone who was born on the internet has had some mutual experience growing up on the internet. Most people grew up with one very important game, which was Club Penguin. There are so many records of it, though not necessarily a place on the internet, it is a collective identity for Gen-Z folks who grew up on choppy computers. It was an escape and a form of entertainment geared fully toward children in the late 2000s. These places are becoming increasingly scarce as more and more companies commercialise the web, and places for children are being reduced. This points to another aspect of the Dead Internet Theory, places for kids are going away." },
{ id: 7, label: "AETHER", type: "data", x: 0.66, y: 0.88, color: "#42e5ff", radius: 14,
desc: "Atmospheric Records",
fullLore: "The lost media of the Internet – The most interesting spot on the Internet is the urban legends of the net. That leads to the most infamous image of Jack the Killer, whose origin we don’t know. The uploaded image is an edited version of the course, but even then, the original poster is unsure where it is from. Thus making it lost media. We can go back further and further into lost media, but this is the most infamous one in the lost media community." },
{ id: 8, label: "SINGULARITY", type: "core", x: 0.5, y: 0.45, color: "#ffffff", radius: 21,
desc: "Active Archive Core — pulse",
fullLore: "The Politics of the Internet – The most impressive feat of the Internet, however, is the liberalisation of knowledge. For this entry, I want to provide the most important form of this: https://wikileaks.org/. A database of information, uncensored and available both on the surface and dark web by Journalist Julian Assange. It serves against authoritarian coverage of information and liberates information and abuse of power from all over the world." }
];
// Edges / connections between nodes
const edges = [
{ from: 0, to: 1 }, { from: 0, to: 2 }, { from: 0, to: 8 },
{ from: 1, to: 4 }, { from: 1, to: 8 },
{ from: 2, to: 5 }, { from: 2, to: 8 },
{ from: 3, to: 4 }, { from: 3, to: 5 }, { from: 3, to: 6 }, { from: 3, to: 7 }, { from: 3, to: 8 },
{ from: 4, to: 6 }, { from: 5, to: 7 }, { from: 6, to: 7 }, { from: 8, to: 3 }, { from: 1, to: 3 }, { from: 2, to: 3 }
];
let time = 0;
let hoveredNode = null;
let animFrame;
// Helper: get canvas absolute positions
function getNodePos(node) {
return { x: node.x * width, y: node.y * height };
}
function resizeCanvas() {
const container = canvas.parentElement;
const containerWidth = container.clientWidth;
let drawWidth = Math.min(containerWidth, 1000);
let drawHeight = (drawWidth * 550) / 900;
canvas.style.width = `${drawWidth}px`;
canvas.style.height = `${drawHeight}px`;
canvas.width = 900;
canvas.height = 550;
width = 900;
height = 550;
drawMap();
}
function drawMap() {
if (!ctx) return;
ctx.clearRect(0, 0, width, height);
// subtle grid
ctx.save();
ctx.globalAlpha = 0.2;
ctx.strokeStyle = "#2a6f8f";
ctx.lineWidth = 0.6;
for (let i = 0; i < width; i += 40) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i % height);
ctx.lineTo(width, i % height);
ctx.stroke();
}
ctx.restore();
// draw edges
for (let edge of edges) {
const fromNode = nodes[edge.from];
const toNode = nodes[edge.to];
if (fromNode && toNode) {
const p1 = getNodePos(fromNode);
const p2 = getNodePos(toNode);
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
const gradient = ctx.createLinearGradient(p1.x, p1.y, p2.x, p2.y);
gradient.addColorStop(0, fromNode.color || "#6ab0ff");
gradient.addColorStop(1, toNode.color || "#c084fc");
ctx.strokeStyle = gradient;
ctx.lineWidth = 1.8;
ctx.shadowBlur = 3;
ctx.shadowColor = "rgba(0,200,255,0.4)";
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
ctx.strokeStyle = "rgba(150,220,255,0.25)";
ctx.lineWidth = 3;
ctx.setLineDash([6, 8]);
ctx.stroke();
ctx.setLineDash([]);
}
}
ctx.shadowBlur = 0;
// draw nodes with pulse & hover
for (let node of nodes) {
const pos = getNodePos(node);
let radius = node.radius;
let isHover = (hoveredNode === node.id);
let pulse = 0;
if (node.type === 'core' || node.id === 8) {
pulse = Math.sin(time * 0.008) * 2.5;
} else if (node.type === 'link') {
pulse = Math.sin(time * 0.012) * 1.8;
} else {
pulse = Math.sin(time * 0.005 + node.id) * 1.2;
}
let finalRadius = radius + (isHover ? 5 : pulse);
if (isHover) finalRadius += 2;
ctx.shadowBlur = 15;
ctx.shadowColor = node.color + "cc";
let grad = ctx.createRadialGradient(pos.x-3, pos.y-3, 3, pos.x, pos.y, finalRadius);
grad.addColorStop(0, node.color);
grad.addColorStop(1, "#1a2a44");
ctx.fillStyle = grad;
ctx.beginPath();
ctx.arc(pos.x, pos.y, finalRadius, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(pos.x-1, pos.y-1, finalRadius*0.35, 0, Math.PI*2);
ctx.fillStyle = "rgba(255,255,245,0.8)";
ctx.fill();
ctx.font = `bold ${Math.min(14 + (isHover?2:0), 16)}px "Segoe UI", monospace`;
ctx.fillStyle = "#f0fcff";
ctx.shadowBlur = 4;
ctx.fillText(node.label, pos.x - (node.label.length*3.2), pos.y - finalRadius - 4);
if (isHover) {
ctx.font = "11px monospace";
ctx.fillStyle = "#aaffdd";
ctx.fillText("⨯ tap to reveal full lore", pos.x - 50, pos.y + finalRadius + 12);
}
}
ctx.shadowBlur = 0;
// extra ring for singularity
const singularityNode = nodes.find(n => n.id === 8);
if(singularityNode) {
const pos = getNodePos(singularityNode);
ctx.beginPath();
ctx.arc(pos.x, pos.y, singularityNode.radius + 5 + Math.sin(time*0.015)*2, 0, Math.PI*2);
ctx.strokeStyle = "#fffa9e";
ctx.lineWidth = 1.8;
ctx.stroke();
}
}
// ----- FUNCTION: HIDE NODE MAP & SHOW INFO PANEL (when node pressed) -----
const mapContainer = document.getElementById('mapContainer');
const infoPanel = document.getElementById('infoPanel');
const panelTitle = document.getElementById('panelTitle');
const panelType = document.getElementById('panelType');
const panelDescription = document.getElementById('panelDescription');
const closePanelBtn = document.getElementById('closeInfoPanel');
// Reveal information for a given node ID
function revealNodeInfo(nodeId) {
const node = nodes.find(n => n.id === nodeId);
if (!node) return;
// Hide the node-map container, show info panel
mapContainer.classList.add('hidden-map');
infoPanel.classList.remove('hidden-panel');
// Populate panel with node's rich lore
panelTitle.textContent = node.label;
let typeText = "";
if (node.type === "core") typeText = "⚜️ MEMORY CORE ⚜️";
else if (node.type === "data") typeText = "📡 DATA CLUSTER 📡";
else if (node.type === "link") typeText = "🔗 QUANTUM LINK 🔗";
panelType.textContent = typeText;
panelDescription.textContent = node.fullLore;
// optional: small easter egg effect
}
// Return to map view: hide info panel, show map container
function returnToNodeMap() {
infoPanel.classList.add('hidden-panel');
mapContainer.classList.remove('hidden-map');
// reset hover after return
hoveredNode = null;
const statusDiv = document.getElementById('nodeStatus');
if (statusDiv) statusDiv.innerHTML = `🌀 tap any node → <span>reveal archived knowledge</span>`;
drawMap();
}
// Close button event
if (closePanelBtn) {
closePanelBtn.addEventListener('click', returnToNodeMap);
}
// ---- Intercept node clicks on canvas (press/tap) ----
function handleNodeClick(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
let clientX, clientY;
if (e.touches) {
// touch event
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
e.preventDefault();
} else {
clientX = e.clientX;
clientY = e.clientY;
}
const canvasX = (clientX - rect.left) * scaleX;
const canvasY = (clientY - rect.top) * scaleY;
// check which node is clicked
for (let node of nodes) {
const pos = getNodePos(node);
const radius = node.radius + 10; // generous hit area
const dx = canvasX - pos.x;
const dy = canvasY - pos.y;
if (Math.hypot(dx, dy) <= radius) {
// Node pressed -> trigger info reveal
revealNodeInfo(node.id);
break;
}
}
}
// Mouse & touch handlers for click
canvas.addEventListener('click', handleNodeClick);
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0];
const fakeEvent = { clientX: touch.clientX, clientY: touch.clientY, touches: e.touches };
handleNodeClick(fakeEvent);
});
// hover handling (for visual only)
function handleCanvasHover(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
let clientX = e.clientX;
let clientY = e.clientY;
if (e.touches) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
}
const canvasX = (clientX - rect.left) * scaleX;
const canvasY = (clientY - rect.top) * scaleY;
let newHover = null;
for (let node of nodes) {
const pos = getNodePos(node);
const radius = node.radius + 6;
if (Math.hypot(canvasX - pos.x, canvasY - pos.y) <= radius) {
newHover = node.id;
break;
}
}
if (hoveredNode !== newHover) {
hoveredNode = newHover;
const statusDiv = document.getElementById('nodeStatus');
if (statusDiv) {
if (hoveredNode !== null) {
const nodeObj = nodes.find(n => n.id === hoveredNode);
statusDiv.innerHTML = `🌀 <span>${nodeObj.label}</span> — ${nodeObj.desc} (tap to unlock lore)`;
} else {
statusDiv.innerHTML = `🌀 tap any node → <span>reveal archived knowledge</span>`;
}
}
drawMap();
} else {
drawMap();
}
}
canvas.addEventListener('mousemove', handleCanvasHover);
canvas.addEventListener('mouseleave', () => {
hoveredNode = null;
const statusDiv = document.getElementById('nodeStatus');
if (statusDiv) statusDiv.innerHTML = `🌀 tap any node → <span>reveal archived knowledge</span>`;
drawMap();
});
// animation loop
function animateNodes() {
time = (time + 1) % (Math.PI * 200);
drawMap();
animFrame = requestAnimationFrame(animateNodes);
}
window.addEventListener('resize', () => {
resizeCanvas();
drawMap();
});
resizeCanvas();
animateNodes();
// ensure if info panel is open by accident, map is hidden accordingly, but default hidden class works.
// also if someone clicks return, map reappears properly.
})();