idiotik.js 1.0.15 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/idiotik.js +410 -560
package/src/idiotik.js CHANGED
@@ -2,45 +2,44 @@
2
2
  * idiotik.js — youareanidiot-style chaos toolkit
3
3
  * recursive by default. you were warned.
4
4
  *
5
- * CHANGES v2:
6
- * - idiotik.start() with no args (or called after queuing) fires ALL queued tasks at once
7
- * - idiotik.start(n) fires the last n queued tasks (number overload)
8
- * - tasks are auto-queued when you call idiotik.effect(), idiotik.say(), etc. without wrapping in start()
9
- * - recursive mode is INSTANT: spawns a new window the moment the page loads, not on an event
10
- * - beforeunload + unload + visibilitychange + pagehide ALL spawn more windows to prevent escape
11
- * - new effects: pixelate, zoom, bounce, vhs, static, darkmode, flip, blur
12
- * - new chaos methods: ghost(), keylogger(), fakeCursor(), gravityWarp(), haunt()
13
- * - audio: distortion node added to chain, white noise generator
14
- * - cursor: new "ghost" and "eyes" presets
15
- * - idiotik.nuke() — kills everything idiotik created
16
- * - idiotik.status() — logs what's currently running
5
+ * CHANGES v4:
6
+ * - idiotik.start("preset:name") — queue a preset
7
+ * - idiotik.start("effect:name") — queue an effect
8
+ * - idiotik.start("say:message") — queue a popup/alert
9
+ * - idiotik.start("cursor:name") — queue a cursor
10
+ * - idiotik.start("scream") — queue scream
11
+ * - idiotik.start("freeze") — queue freeze
12
+ * - idiotik.start("fullscreen") — queue fullscreen
13
+ * - idiotik.start("haunt") — queue haunt
14
+ * - idiotik.start("fakecursor") — queue fakeCursor
15
+ * - idiotik.start("gravitywarp") — queue gravityWarp
16
+ * - idiotik.start("redirect:url") — queue redirect
17
+ * - idiotik.start("audio:url") — queue audio
18
+ * - idiotik.start("jumpscare:url") — queue jumpscare
19
+ * - idiotik.start(n) — fire task at 1-based queue position n
20
+ * - idiotik.start() — fire ALL queued tasks, then clear queue
21
+ * - idiotik.startnow("...") — same DSL as start() but fires immediately
22
+ * - idiotik.click(target) — bind queued tasks to a click target
23
+ * - idiotik.nuke() — kill everything
17
24
  */
18
25
 
19
26
  (function (global) {
20
27
 
21
28
  // ─── GLOBAL STATE ───────────────────────────────────────────────────────────
22
- let _recursive = true;
23
- let _volume = 100;
24
- let _popup = true;
29
+ let _recursive = true;
30
+ let _volume = 100;
31
+ let _popup = true;
25
32
  let _activeScripts = {};
26
33
  let _activeEffects = new Set();
27
- let _audioCtx = null;
28
- let _scriptStack = [];
29
-
30
- // NEW: task queue — every action registers here; idiotik.start() fires them
31
- let _taskQueue = [];
32
-
33
- // NEW: track all spawned windows so we can spam them
34
- let _spawnedWindows = [];
35
-
36
- // NEW: track all global intervals/timeouts for nuke()
34
+ let _audioCtx = null;
35
+ let _scriptStack = [];
36
+ let _taskQueue = [];
37
+ let _spawnedWindows = [];
37
38
  let _globalIntervals = [];
38
39
  let _globalTimeouts = [];
39
40
  let _globalNodes = [];
40
41
 
41
42
  // ─── RECURSIVE SPAWN ENGINE ─────────────────────────────────────────────────
42
- // Called once on load when recursive=true, and on every unload attempt.
43
- // Spawns a new copy of the current page in a popup immediately.
44
43
  function _spawnWindow() {
45
44
  try {
46
45
  const w = window.open(window.location.href, "_blank",
@@ -50,12 +49,8 @@
50
49
  );
51
50
  if (w) {
52
51
  _spawnedWindows.push(w);
53
- // ping alive children and respawn any that closed
54
52
  const id = setInterval(() => {
55
- if (w.closed) {
56
- clearInterval(id);
57
- if (_recursive) _spawnWindow();
58
- }
53
+ if (w.closed) { clearInterval(id); if (_recursive) _spawnWindow(); }
59
54
  }, 500);
60
55
  _globalIntervals.push(id);
61
56
  }
@@ -64,175 +59,25 @@
64
59
 
65
60
  function _initRecursion() {
66
61
  if (!_recursive) return;
67
-
68
- // Spawn IMMEDIATELY — don't wait for any event
69
62
  _spawnWindow();
70
-
71
- // Also spawn on every possible exit path
72
63
  const _blockAndSpawn = function(e) {
73
64
  if (!_recursive) return;
74
- _spawnWindow();
75
- _spawnWindow(); // double-tap
76
- e.preventDefault();
77
- e.returnValue = "";
78
- return "";
65
+ _spawnWindow(); _spawnWindow();
66
+ e.preventDefault(); e.returnValue = ""; return "";
79
67
  };
80
-
81
- window.addEventListener("beforeunload", _blockAndSpawn, { capture: true });
82
- window.addEventListener("unload", () => { if (_recursive) { _spawnWindow(); _spawnWindow(); } }, { capture: true });
83
- window.addEventListener("pagehide", () => { if (_recursive) { _spawnWindow(); _spawnWindow(); } }, { capture: true });
84
- document.addEventListener("visibilitychange", () => {
85
- if (document.hidden && _recursive) {
86
- _spawnWindow();
87
- _spawnWindow();
88
- }
89
- });
90
-
91
- // Keep an eye on focus — if user tries to leave, spawn
92
- window.addEventListener("blur", () => {
93
- if (_recursive) setTimeout(_spawnWindow, 50);
94
- });
68
+ window.addEventListener("beforeunload", _blockAndSpawn, { capture: true });
69
+ window.addEventListener("unload", () => { if (_recursive) { _spawnWindow(); _spawnWindow(); } }, { capture: true });
70
+ window.addEventListener("pagehide", () => { if (_recursive) { _spawnWindow(); _spawnWindow(); } }, { capture: true });
71
+ document.addEventListener("visibilitychange", () => { if (document.hidden && _recursive) { _spawnWindow(); _spawnWindow(); } });
72
+ window.addEventListener("blur", () => { if (_recursive) setTimeout(_spawnWindow, 50); });
95
73
  }
96
74
 
97
- // Run recursion engine as soon as DOM is ready
98
75
  if (document.readyState === "loading") {
99
76
  document.addEventListener("DOMContentLoaded", _initRecursion);
100
77
  } else {
101
- // Already loaded — fire immediately
102
78
  setTimeout(_initRecursion, 0);
103
79
  }
104
80
 
105
- // ─── PRESET REGISTRY ────────────────────────────────────────────────────────
106
- const _presets = {
107
- classic: function() {
108
- idiotik.recursive("yes");
109
- idiotik.start(idiotik.effect("rainbow"))();
110
- idiotik.start(idiotik.effect("shake"))();
111
- idiotik.start(idiotik.say("YOU ARE AN IDIOT HA HA HA HA HA HA"));
112
- idiotik.repeat("inf");
113
- idiotik.continue;
114
- idiotik.start(idiotik.scream())();
115
- },
116
- nuclear: function() {
117
- idiotik.recursive("yes");
118
- idiotik.volume(5000);
119
- idiotik.start(idiotik.effect("shake"))();
120
- idiotik.start(idiotik.effect("rainbow"))();
121
- idiotik.start(idiotik.effect("glitch"))();
122
- idiotik.start(idiotik.effect("spin"))();
123
- idiotik.start(idiotik.effect("matrix"))();
124
- idiotik.start(idiotik.effect("vhs"))();
125
- idiotik.start(idiotik.effect("static"))();
126
- idiotik.start(idiotik.effect("zoom"))();
127
- idiotik.start(idiotik.scream())();
128
- idiotik.start(idiotik.fullscreen())();
129
- idiotik.start(idiotik.freeze())();
130
- idiotik.start(idiotik.cursor("none"))();
131
- idiotik.start(idiotik.haunt())();
132
- idiotik.start(idiotik.say("💀💀💀 YOU ARE SO COOKED 💀💀💀"));
133
- idiotik.repeat("inf");
134
- },
135
- mild: function() {
136
- idiotik.recursive("no");
137
- idiotik.volume(100);
138
- idiotik.start(idiotik.effect("shake"))();
139
- idiotik.start(idiotik.say("gotcha lol"));
140
- idiotik.repeat(5);
141
- },
142
- rickroll: function() {
143
- idiotik.recursive("no");
144
- idiotik.start(idiotik.redirect("https://www.youtube.com/watch?v=dQw4w9WgXcQ"))();
145
- },
146
- jumpscare: function() {
147
- idiotik.recursive("yes");
148
- idiotik.volume(300);
149
- idiotik.wait(3000).then(() => {
150
- idiotik.start(idiotik.effect("glitch"))();
151
- idiotik.start(idiotik.jumpscare(
152
- "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/640px-Camponotus_flavomarginatus_ant.jpg"
153
- ))();
154
- idiotik.start(idiotik.scream())();
155
- });
156
- },
157
- epilepsy: function() {
158
- idiotik.recursive("yes");
159
- idiotik.volume(450);
160
- idiotik.start(idiotik.effect("rainbow"))();
161
- idiotik.start(idiotik.effect("shake"))();
162
- idiotik.start(idiotik.effect("glitch"))();
163
- idiotik.start(idiotik.effect("invert"))();
164
- idiotik.start(idiotik.effect("static"))();
165
- idiotik.start(idiotik.cursor("rainbow"))();
166
- idiotik.start(idiotik.scream())();
167
- },
168
- robux: function() {
169
- idiotik.recursive("yes");
170
- idiotik.volume(200);
171
- idiotik.script();
172
- idiotik.id("robux");
173
- idiotik.start(idiotik.say("FREE ROBUX AT PORNHUB.COM!!!"));
174
- idiotik.repeat("inf");
175
- idiotik.continue;
176
- idiotik.end();
177
- idiotik.start(idiotik.effect("rainbow"))();
178
- idiotik.start(idiotik.cursor("explode"))();
179
- },
180
- villain: function() {
181
- idiotik.recursive("no");
182
- idiotik.volume(150);
183
- idiotik.start(idiotik.effect("matrix"))();
184
- idiotik.wait(2000).then(() => {
185
- idiotik.start(idiotik.effect("glitch"))();
186
- });
187
- idiotik.wait(4000).then(() => {
188
- idiotik.volume(500);
189
- idiotik.start(idiotik.effect("shake"))();
190
- idiotik.start(idiotik.scream())();
191
- idiotik.recursive("yes");
192
- idiotik.start(idiotik.say("did you really think you were safe 🗿"));
193
- idiotik.repeat("inf");
194
- });
195
- },
196
- melt: function() {
197
- idiotik.recursive("no");
198
- idiotik.volume(100);
199
- idiotik.start(idiotik.effect("melt"))();
200
- idiotik.start(idiotik.effect("spin"))();
201
- idiotik.wait(3000).then(() => {
202
- idiotik.volume(300);
203
- idiotik.start(idiotik.effect("rainbow"))();
204
- idiotik.start(idiotik.scream())();
205
- });
206
- },
207
- haunted: function() {
208
- idiotik.recursive("yes");
209
- idiotik.volume(80);
210
- idiotik.start(idiotik.effect("vhs"))();
211
- idiotik.start(idiotik.effect("glitch"))();
212
- idiotik.start(idiotik.cursor("ghost"))();
213
- idiotik.start(idiotik.haunt())();
214
- idiotik.wait(5000).then(() => {
215
- idiotik.volume(600);
216
- idiotik.start(idiotik.scream())();
217
- idiotik.start(idiotik.effect("flip"))();
218
- idiotik.start(idiotik.effect("shake"))();
219
- idiotik.start(idiotik.say("👁️ i was here the whole time 👁️"));
220
- idiotik.repeat("inf");
221
- });
222
- },
223
- gravity: function() {
224
- idiotik.recursive("yes");
225
- idiotik.volume(200);
226
- idiotik.start(idiotik.gravityWarp())();
227
- idiotik.start(idiotik.effect("melt"))();
228
- idiotik.start(idiotik.cursor("eyes"))();
229
- idiotik.wait(3000).then(() => {
230
- idiotik.start(idiotik.effect("rainbow"))();
231
- idiotik.start(idiotik.scream())();
232
- });
233
- },
234
- };
235
-
236
81
  // ─── HELPERS ────────────────────────────────────────────────────────────────
237
82
  function _getAudioCtx() {
238
83
  if (!_audioCtx) _audioCtx = new (window.AudioContext || window.webkitAudioContext)();
@@ -253,7 +98,6 @@
253
98
  ctx[type] = ctx[type] || [];
254
99
  ctx[type].push(thing);
255
100
  } else {
256
- // track globally for nuke()
257
101
  if (type === "intervals") _globalIntervals.push(thing);
258
102
  if (type === "timeouts") _globalTimeouts.push(thing);
259
103
  if (type === "htmlNodes") _globalNodes.push(thing);
@@ -267,26 +111,16 @@
267
111
  source.loop = true;
268
112
  const gainNode = ctx.createGain();
269
113
  const rawVol = _volume;
270
- if (rawVol <= 200) {
271
- gainNode.gain.value = rawVol / 100;
272
- } else {
273
- gainNode.gain.value = Math.min(rawVol / 100, 50);
274
- }
114
+ gainNode.gain.value = rawVol <= 200 ? rawVol / 100 : Math.min(rawVol / 100, 50);
275
115
  let lastNode = source;
276
-
277
- // Bass boost
278
116
  if (rawVol > 200) {
279
- const bassBoost = ctx.createBiquadFilter();
280
- bassBoost.type = "lowshelf";
281
- bassBoost.frequency.value = 200;
282
- bassBoost.gain.value = Math.min(((rawVol - 200) / 4800) * 40, 40);
283
- lastNode.connect(bassBoost);
284
- lastNode = bassBoost;
117
+ const bb = ctx.createBiquadFilter();
118
+ bb.type = "lowshelf"; bb.frequency.value = 200;
119
+ bb.gain.value = Math.min(((rawVol - 200) / 4800) * 40, 40);
120
+ lastNode.connect(bb); lastNode = bb;
285
121
  }
286
-
287
- // Full EQ massacre
288
122
  if (rawVol >= 450) {
289
- const bands = [
123
+ for (const b of [
290
124
  { type: "lowshelf", freq: 60, gain: 40 },
291
125
  { type: "peaking", freq: 250, gain: 40 },
292
126
  { type: "peaking", freq: 500, gain: 40 },
@@ -294,18 +128,12 @@
294
128
  { type: "peaking", freq: 4000, gain: 40 },
295
129
  { type: "peaking", freq: 8000, gain: 40 },
296
130
  { type: "highshelf", freq: 16000, gain: 40 },
297
- ];
298
- for (const b of bands) {
131
+ ]) {
299
132
  const f = ctx.createBiquadFilter();
300
- f.type = b.type;
301
- f.frequency.value = b.freq;
302
- f.gain.value = b.gain;
303
- lastNode.connect(f);
304
- lastNode = f;
133
+ f.type = b.type; f.frequency.value = b.freq; f.gain.value = b.gain;
134
+ lastNode.connect(f); lastNode = f;
305
135
  }
306
136
  }
307
-
308
- // NEW: distortion at high volumes
309
137
  if (rawVol >= 300) {
310
138
  const dist = ctx.createWaveShaper();
311
139
  const curve = new Float32Array(256);
@@ -314,12 +142,9 @@
314
142
  const x = (i * 2) / 256 - 1;
315
143
  curve[i] = ((Math.PI + k) * x) / (Math.PI + k * Math.abs(x));
316
144
  }
317
- dist.curve = curve;
318
- dist.oversample = "4x";
319
- lastNode.connect(dist);
320
- lastNode = dist;
145
+ dist.curve = curve; dist.oversample = "4x";
146
+ lastNode.connect(dist); lastNode = dist;
321
147
  }
322
-
323
148
  lastNode.connect(gainNode);
324
149
  gainNode.connect(ctx.destination);
325
150
  return source;
@@ -336,13 +161,11 @@
336
161
  }
337
162
 
338
163
  function _parseAudioUrl(urlOrFile) {
339
- if (typeof urlOrFile === "string" && urlOrFile.startsWith("file://")) {
164
+ if (typeof urlOrFile === "string" && urlOrFile.startsWith("file://"))
340
165
  return urlOrFile.replace("file://", "");
341
- }
342
166
  return urlOrFile;
343
167
  }
344
168
 
345
- // NEW: white noise generator
346
169
  function _whiteNoise(durationSec) {
347
170
  const ctx = _getAudioCtx();
348
171
  const bufferSize = ctx.sampleRate * durationSec;
@@ -358,248 +181,296 @@
358
181
  return source;
359
182
  }
360
183
 
184
+ // ─── DSL PARSER ─────────────────────────────────────────────────────────────
185
+ // Turns "effect:shake", "say:hello world", "preset:robux", "scream", etc.
186
+ // into a callable function. Used by both start() and startnow().
187
+ function _resolve(ref) {
188
+ // already a function — use directly
189
+ if (typeof ref === "function") return ref;
190
+
191
+ // string DSL
192
+ if (typeof ref === "string") {
193
+ const colon = ref.indexOf(":");
194
+ const cmd = colon === -1 ? ref.trim().toLowerCase() : ref.slice(0, colon).trim().toLowerCase();
195
+ const arg = colon === -1 ? "" : ref.slice(colon + 1);
196
+
197
+ switch (cmd) {
198
+ case "effect": return idiotik.effect(arg);
199
+ case "say": return idiotik.say(arg);
200
+ case "cursor": return idiotik.cursor(arg);
201
+ case "preset": return idiotik.preset(arg);
202
+ case "redirect": return idiotik.redirect(arg);
203
+ case "audio": return idiotik.audio(arg);
204
+ case "jumpscare": return idiotik.jumpscare(arg);
205
+ case "scream": return idiotik.scream();
206
+ case "freeze": return idiotik.freeze();
207
+ case "fullscreen": return idiotik.fullscreen();
208
+ case "haunt": return idiotik.haunt();
209
+ case "fakecursor": return idiotik.fakeCursor();
210
+ case "gravitywarp":return idiotik.gravityWarp();
211
+ case "noise": return idiotik.noise(Number(arg) || 1);
212
+ default:
213
+ console.warn(`idiotik.start: unknown command "${cmd}"`);
214
+ return null;
215
+ }
216
+ }
217
+
218
+ return null;
219
+ }
220
+
361
221
  // ─── CURSOR PRESETS ─────────────────────────────────────────────────────────
362
222
  const _cursorPresets = {
363
- hand: "pointer",
364
- none: "none",
365
- crosshair: "crosshair",
366
- wait: "wait",
367
- rainbow: "__rainbow__",
368
- wiggle: "__wiggle__",
369
- explode: "__explode__",
370
- ghost: "__ghost__", // NEW
371
- eyes: "__eyes__", // NEW
223
+ hand: "pointer", none: "none", crosshair: "crosshair", wait: "wait",
224
+ rainbow: "__rainbow__", wiggle: "__wiggle__", explode: "__explode__",
225
+ ghost: "__ghost__", eyes: "__eyes__",
372
226
  };
373
227
 
374
228
  // ─── EFFECT IMPLEMENTATIONS ─────────────────────────────────────────────────
375
229
  const _effectImpls = {
376
230
  shake() {
377
- const styleId = "__idiotik_shake__";
378
- if (document.getElementById(styleId)) return;
379
- const s = document.createElement("style");
380
- s.id = styleId;
381
- s.textContent = `
382
- @keyframes __idiotik_shake { 0%,100%{transform:translate(0,0)} 10%{transform:translate(-8px,8px)} 20%{transform:translate(8px,-8px)} 30%{transform:translate(-8px,0)} 40%{transform:translate(8px,8px)} 50%{transform:translate(-4px,-4px)} 60%{transform:translate(4px,4px)} 70%{transform:translate(-8px,8px)} 80%{transform:translate(8px,-8px)} 90%{transform:translate(-4px,4px)} }
383
- body { animation: __idiotik_shake 0.3s infinite; }
384
- `;
231
+ const id = "__idiotik_shake__"; if (document.getElementById(id)) return;
232
+ const s = document.createElement("style"); s.id = id;
233
+ s.textContent = `@keyframes __idiotik_shake{0%,100%{transform:translate(0,0)}10%{transform:translate(-8px,8px)}20%{transform:translate(8px,-8px)}30%{transform:translate(-8px,0)}40%{transform:translate(8px,8px)}50%{transform:translate(-4px,-4px)}60%{transform:translate(4px,4px)}70%{transform:translate(-8px,8px)}80%{transform:translate(8px,-8px)}90%{transform:translate(-4px,4px)}}body{animation:__idiotik_shake 0.3s infinite}`;
385
234
  document.head.appendChild(s);
386
235
  },
387
236
  spin() {
388
- const styleId = "__idiotik_spin__";
389
- if (document.getElementById(styleId)) return;
390
- const s = document.createElement("style");
391
- s.id = styleId;
392
- s.textContent = `
393
- @keyframes __idiotik_spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
394
- body { animation: __idiotik_spin 2s linear infinite; transform-origin: center center; }
395
- `;
237
+ const id = "__idiotik_spin__"; if (document.getElementById(id)) return;
238
+ const s = document.createElement("style"); s.id = id;
239
+ s.textContent = `@keyframes __idiotik_spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}body{animation:__idiotik_spin 2s linear infinite;transform-origin:center center}`;
396
240
  document.head.appendChild(s);
397
241
  },
398
242
  glitch() {
399
- const styleId = "__idiotik_glitch__";
400
- if (document.getElementById(styleId)) return;
401
- const s = document.createElement("style");
402
- s.id = styleId;
403
- s.textContent = `
404
- @keyframes __idiotik_glitch1 { 0%,100%{clip-path:inset(0 0 95% 0)} 25%{clip-path:inset(30% 0 50% 0)} 50%{clip-path:inset(60% 0 20% 0)} 75%{clip-path:inset(10% 0 80% 0)} }
405
- @keyframes __idiotik_glitch2 { 0%,100%{clip-path:inset(80% 0 5% 0);transform:translate(-5px,0)} 25%{clip-path:inset(20% 0 70% 0);transform:translate(5px,0)} 50%{clip-path:inset(50% 0 30% 0);transform:translate(-3px,0)} 75%{clip-path:inset(5% 0 90% 0);transform:translate(3px,0)} }
406
- body::before, body::after { content:''; position:fixed; top:0;left:0;width:100%;height:100%; background:inherit; pointer-events:none; z-index:9999; }
407
- body::before { animation: __idiotik_glitch1 0.4s infinite; color:red; text-shadow:2px 0 red; }
408
- body::after { animation: __idiotik_glitch2 0.4s infinite; color:blue; text-shadow:-2px 0 blue; }
409
- `;
243
+ const id = "__idiotik_glitch__"; if (document.getElementById(id)) return;
244
+ const s = document.createElement("style"); s.id = id;
245
+ s.textContent = `@keyframes __idiotik_glitch1{0%,100%{clip-path:inset(0 0 95% 0)}25%{clip-path:inset(30% 0 50% 0)}50%{clip-path:inset(60% 0 20% 0)}75%{clip-path:inset(10% 0 80% 0)}}@keyframes __idiotik_glitch2{0%,100%{clip-path:inset(80% 0 5% 0);transform:translate(-5px,0)}25%{clip-path:inset(20% 0 70% 0);transform:translate(5px,0)}50%{clip-path:inset(50% 0 30% 0);transform:translate(-3px,0)}75%{clip-path:inset(5% 0 90% 0);transform:translate(3px,0)}}body::before,body::after{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:inherit;pointer-events:none;z-index:9999}body::before{animation:__idiotik_glitch1 0.4s infinite;color:red;text-shadow:2px 0 red}body::after{animation:__idiotik_glitch2 0.4s infinite;color:blue;text-shadow:-2px 0 blue}`;
410
246
  document.head.appendChild(s);
411
247
  },
412
248
  rainbow() {
413
- const styleId = "__idiotik_rainbow__";
414
- if (document.getElementById(styleId)) return;
415
- const s = document.createElement("style");
416
- s.id = styleId;
417
- s.textContent = `
418
- @keyframes __idiotik_rainbow { 0%{background:#ff0000} 15%{background:#ff8800} 30%{background:#ffff00} 45%{background:#00ff00} 60%{background:#0000ff} 75%{background:#8800ff} 90%{background:#ff00ff} 100%{background:#ff0000} }
419
- body { animation: __idiotik_rainbow 0.5s infinite; }
420
- `;
249
+ const id = "__idiotik_rainbow__"; if (document.getElementById(id)) return;
250
+ const s = document.createElement("style"); s.id = id;
251
+ s.textContent = `@keyframes __idiotik_rainbow{0%{background:#ff0000}15%{background:#ff8800}30%{background:#ffff00}45%{background:#00ff00}60%{background:#0000ff}75%{background:#8800ff}90%{background:#ff00ff}100%{background:#ff0000}}body{animation:__idiotik_rainbow 0.5s infinite}`;
421
252
  document.head.appendChild(s);
422
253
  },
423
254
  invert() {
424
- const styleId = "__idiotik_invert__";
425
- if (document.getElementById(styleId)) return;
426
- const s = document.createElement("style");
427
- s.id = styleId;
428
- s.textContent = `body { filter: invert(1); }`;
255
+ const id = "__idiotik_invert__"; if (document.getElementById(id)) return;
256
+ const s = document.createElement("style"); s.id = id;
257
+ s.textContent = `body{filter:invert(1)}`;
429
258
  document.head.appendChild(s);
430
259
  },
431
260
  matrix() {
432
- const canvasId = "__idiotik_matrix__";
433
- if (document.getElementById(canvasId)) return;
434
- const canvas = document.createElement("canvas");
435
- canvas.id = canvasId;
261
+ const cid = "__idiotik_matrix__"; if (document.getElementById(cid)) return;
262
+ const canvas = document.createElement("canvas"); canvas.id = cid;
436
263
  canvas.style.cssText = "position:fixed;top:0;left:0;z-index:9998;pointer-events:none;width:100vw;height:100vh;opacity:0.7;";
437
- document.body.appendChild(canvas);
438
- _track(canvas, "htmlNodes");
264
+ document.body.appendChild(canvas); _track(canvas, "htmlNodes");
439
265
  const ctx = canvas.getContext("2d");
440
- canvas.width = window.innerWidth;
441
- canvas.height = window.innerHeight;
266
+ canvas.width = window.innerWidth; canvas.height = window.innerHeight;
442
267
  const cols = Math.floor(canvas.width / 16);
443
268
  const drops = Array(cols).fill(1);
444
269
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()";
445
- const id = setInterval(() => {
446
- ctx.fillStyle = "rgba(0,0,0,0.05)";
447
- ctx.fillRect(0, 0, canvas.width, canvas.height);
448
- ctx.fillStyle = "#0f0";
449
- ctx.font = "16px monospace";
270
+ _track(setInterval(() => {
271
+ ctx.fillStyle = "rgba(0,0,0,0.05)"; ctx.fillRect(0, 0, canvas.width, canvas.height);
272
+ ctx.fillStyle = "#0f0"; ctx.font = "16px monospace";
450
273
  drops.forEach((y, i) => {
451
274
  const ch = chars[Math.floor(Math.random() * chars.length)];
452
275
  ctx.fillText(ch, i * 16, y * 16);
453
276
  if (y * 16 > canvas.height && Math.random() > 0.975) drops[i] = 0;
454
277
  drops[i]++;
455
278
  });
456
- }, 50);
457
- _track(id, "intervals");
279
+ }, 50), "intervals");
458
280
  },
459
281
  melt() {
460
- const styleId = "__idiotik_melt__";
461
- if (document.getElementById(styleId)) return;
462
- const s = document.createElement("style");
463
- s.id = styleId;
464
- s.textContent = `
465
- @keyframes __idiotik_melt { 0%{transform:skewY(0deg) scaleY(1)} 50%{transform:skewY(5deg) scaleY(1.1)} 100%{transform:skewY(-3deg) scaleY(0.9)} }
466
- body { animation: __idiotik_melt 1s ease-in-out infinite alternate; transform-origin: bottom; }
467
- `;
282
+ const id = "__idiotik_melt__"; if (document.getElementById(id)) return;
283
+ const s = document.createElement("style"); s.id = id;
284
+ s.textContent = `@keyframes __idiotik_melt{0%{transform:skewY(0deg) scaleY(1)}50%{transform:skewY(5deg) scaleY(1.1)}100%{transform:skewY(-3deg) scaleY(0.9)}}body{animation:__idiotik_melt 1s ease-in-out infinite alternate;transform-origin:bottom}`;
468
285
  document.head.appendChild(s);
469
286
  },
470
-
471
- // ── NEW EFFECTS ───────────────────────────────────────────────────────────
472
-
473
287
  vhs() {
474
- // VHS scanlines + tracking error
475
- const styleId = "__idiotik_vhs__";
476
- if (document.getElementById(styleId)) return;
477
- const s = document.createElement("style");
478
- s.id = styleId;
479
- s.textContent = `
480
- @keyframes __idiotik_vhs_scan { 0%{top:-100%} 100%{top:100%} }
481
- @keyframes __idiotik_vhs_track { 0%,95%{transform:none} 96%{transform:translate(-4px,0) skewX(-0.5deg)} 97%{transform:translate(4px,0) skewX(0.5deg)} 98%{transform:translate(-2px,0)} 100%{transform:none} }
482
- body { animation: __idiotik_vhs_track 3s infinite; }
483
- body::before {
484
- content:''; position:fixed; top:0; left:0; width:100%; height:3px;
485
- background:rgba(255,255,255,0.15); z-index:99999; pointer-events:none;
486
- animation: __idiotik_vhs_scan 4s linear infinite;
487
- }
488
- body::after {
489
- content:''; position:fixed; top:0; left:0; width:100%; height:100%;
490
- background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,0,0,0.08) 2px,rgba(0,0,0,0.08) 4px);
491
- pointer-events:none; z-index:99998;
492
- }
493
- `;
288
+ const id = "__idiotik_vhs__"; if (document.getElementById(id)) return;
289
+ const s = document.createElement("style"); s.id = id;
290
+ s.textContent = `@keyframes __idiotik_vhs_scan{0%{top:-100%}100%{top:100%}}@keyframes __idiotik_vhs_track{0%,95%{transform:none}96%{transform:translate(-4px,0) skewX(-0.5deg)}97%{transform:translate(4px,0) skewX(0.5deg)}98%{transform:translate(-2px,0)}100%{transform:none}}body{animation:__idiotik_vhs_track 3s infinite}body::before{content:'';position:fixed;top:0;left:0;width:100%;height:3px;background:rgba(255,255,255,0.15);z-index:99999;pointer-events:none;animation:__idiotik_vhs_scan 4s linear infinite}body::after{content:'';position:fixed;top:0;left:0;width:100%;height:100%;background:repeating-linear-gradient(0deg,transparent,transparent 2px,rgba(0,0,0,0.08) 2px,rgba(0,0,0,0.08) 4px);pointer-events:none;z-index:99998}`;
494
291
  document.head.appendChild(s);
495
292
  },
496
-
497
293
  static() {
498
- // TV static noise overlay on canvas
499
- const canvasId = "__idiotik_static__";
500
- if (document.getElementById(canvasId)) return;
501
- const canvas = document.createElement("canvas");
502
- canvas.id = canvasId;
294
+ const cid = "__idiotik_static__"; if (document.getElementById(cid)) return;
295
+ const canvas = document.createElement("canvas"); canvas.id = cid;
503
296
  canvas.width = 256; canvas.height = 256;
504
297
  canvas.style.cssText = "position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9997;pointer-events:none;opacity:0.12;image-rendering:pixelated;";
505
- document.body.appendChild(canvas);
506
- _track(canvas, "htmlNodes");
298
+ document.body.appendChild(canvas); _track(canvas, "htmlNodes");
507
299
  const ctx = canvas.getContext("2d");
508
- const id = setInterval(() => {
300
+ _track(setInterval(() => {
509
301
  const img = ctx.createImageData(256, 256);
510
302
  for (let i = 0; i < img.data.length; i += 4) {
511
303
  const v = Math.random() * 255 | 0;
512
- img.data[i] = img.data[i+1] = img.data[i+2] = v;
513
- img.data[i+3] = 255;
304
+ img.data[i] = img.data[i+1] = img.data[i+2] = v; img.data[i+3] = 255;
514
305
  }
515
306
  ctx.putImageData(img, 0, 0);
516
- }, 40);
517
- _track(id, "intervals");
307
+ }, 40), "intervals");
518
308
  },
519
-
520
309
  zoom() {
521
- const styleId = "__idiotik_zoom__";
522
- if (document.getElementById(styleId)) return;
523
- const s = document.createElement("style");
524
- s.id = styleId;
525
- s.textContent = `
526
- @keyframes __idiotik_zoom { 0%,100%{transform:scale(1)} 50%{transform:scale(1.08)} }
527
- body { animation: __idiotik_zoom 0.8s ease-in-out infinite; }
528
- `;
310
+ const id = "__idiotik_zoom__"; if (document.getElementById(id)) return;
311
+ const s = document.createElement("style"); s.id = id;
312
+ s.textContent = `@keyframes __idiotik_zoom{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}body{animation:__idiotik_zoom 0.8s ease-in-out infinite}`;
529
313
  document.head.appendChild(s);
530
314
  },
531
-
532
315
  bounce() {
533
- const styleId = "__idiotik_bounce__";
534
- if (document.getElementById(styleId)) return;
535
- const s = document.createElement("style");
536
- s.id = styleId;
537
- s.textContent = `
538
- @keyframes __idiotik_bounce { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-20px)} }
539
- body { animation: __idiotik_bounce 0.5s ease-in-out infinite; }
540
- `;
316
+ const id = "__idiotik_bounce__"; if (document.getElementById(id)) return;
317
+ const s = document.createElement("style"); s.id = id;
318
+ s.textContent = `@keyframes __idiotik_bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-20px)}}body{animation:__idiotik_bounce 0.5s ease-in-out infinite}`;
541
319
  document.head.appendChild(s);
542
320
  },
543
-
544
321
  flip() {
545
- const styleId = "__idiotik_flip__";
546
- if (document.getElementById(styleId)) return;
547
- const s = document.createElement("style");
548
- s.id = styleId;
549
- s.textContent = `
550
- @keyframes __idiotik_flip { 0%,100%{transform:scaleX(1)} 50%{transform:scaleX(-1)} }
551
- body { animation: __idiotik_flip 1s step-end infinite; }
552
- `;
322
+ const id = "__idiotik_flip__"; if (document.getElementById(id)) return;
323
+ const s = document.createElement("style"); s.id = id;
324
+ s.textContent = `@keyframes __idiotik_flip{0%,100%{transform:scaleX(1)}50%{transform:scaleX(-1)}}body{animation:__idiotik_flip 1s step-end infinite}`;
553
325
  document.head.appendChild(s);
554
326
  },
555
-
556
327
  blur() {
557
- const styleId = "__idiotik_blur__";
558
- if (document.getElementById(styleId)) return;
559
- const s = document.createElement("style");
560
- s.id = styleId;
561
- s.textContent = `
562
- @keyframes __idiotik_blur { 0%,100%{filter:blur(0px)} 50%{filter:blur(8px)} }
563
- body { animation: __idiotik_blur 1s ease-in-out infinite; }
564
- `;
328
+ const id = "__idiotik_blur__"; if (document.getElementById(id)) return;
329
+ const s = document.createElement("style"); s.id = id;
330
+ s.textContent = `@keyframes __idiotik_blur{0%,100%{filter:blur(0px)}50%{filter:blur(8px)}}body{animation:__idiotik_blur 1s ease-in-out infinite}`;
565
331
  document.head.appendChild(s);
566
332
  },
567
-
568
333
  darkmode() {
569
- const styleId = "__idiotik_darkmode__";
570
- if (document.getElementById(styleId)) return;
571
- const s = document.createElement("style");
572
- s.id = styleId;
573
- s.textContent = `
574
- @keyframes __idiotik_dark { 0%,100%{filter:brightness(1)} 50%{filter:brightness(0)} }
575
- body { animation: __idiotik_dark 0.3s step-end infinite; }
576
- `;
334
+ const id = "__idiotik_darkmode__"; if (document.getElementById(id)) return;
335
+ const s = document.createElement("style"); s.id = id;
336
+ s.textContent = `@keyframes __idiotik_dark{0%,100%{filter:brightness(1)}50%{filter:brightness(0)}}body{animation:__idiotik_dark 0.3s step-end infinite}`;
577
337
  document.head.appendChild(s);
578
338
  },
579
-
580
339
  pixelate() {
581
- // Shrink a canvas copy on top of page
582
- const canvasId = "__idiotik_pixelate__";
583
- if (document.getElementById(canvasId)) return;
584
- const canvas = document.createElement("canvas");
585
- canvas.id = canvasId;
340
+ const cid = "__idiotik_pixelate__"; if (document.getElementById(cid)) return;
341
+ const canvas = document.createElement("canvas"); canvas.id = cid;
586
342
  canvas.style.cssText = "position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9996;pointer-events:none;image-rendering:pixelated;";
587
- canvas.width = 64;
588
- canvas.height = 36;
589
- document.body.appendChild(canvas);
590
- _track(canvas, "htmlNodes");
343
+ canvas.width = 64; canvas.height = 36;
344
+ document.body.appendChild(canvas); _track(canvas, "htmlNodes");
591
345
  const ctx = canvas.getContext("2d");
592
346
  let level = 0;
593
- const id = setInterval(() => {
594
- try {
595
- ctx.drawImage(document.documentElement, 0, 0, 64, 36);
596
- } catch(e) {
597
- ctx.fillStyle = `hsl(${level * 10},100%,50%)`;
598
- ctx.fillRect(0, 0, 64, 36);
599
- }
347
+ _track(setInterval(() => {
348
+ try { ctx.drawImage(document.documentElement, 0, 0, 64, 36); }
349
+ catch(e) { ctx.fillStyle = `hsl(${level * 10},100%,50%)`; ctx.fillRect(0, 0, 64, 36); }
600
350
  level = (level + 1) % 36;
601
- }, 100);
602
- _track(id, "intervals");
351
+ }, 100), "intervals");
352
+ },
353
+ };
354
+
355
+ // ─── PRESET REGISTRY ────────────────────────────────────────────────────────
356
+ const _presets = {
357
+ classic() {
358
+ idiotik.recursive("yes");
359
+ idiotik.startnow("effect:rainbow");
360
+ idiotik.startnow("effect:shake");
361
+ idiotik.start("say:YOU ARE AN IDIOT HA HA HA HA HA HA");
362
+ idiotik.repeat("inf");
363
+ idiotik.startnow("scream");
364
+ },
365
+ nuclear() {
366
+ idiotik.recursive("yes");
367
+ idiotik.volume(5000);
368
+ idiotik.startnow("effect:shake");
369
+ idiotik.startnow("effect:rainbow");
370
+ idiotik.startnow("effect:glitch");
371
+ idiotik.startnow("effect:spin");
372
+ idiotik.startnow("effect:matrix");
373
+ idiotik.startnow("effect:vhs");
374
+ idiotik.startnow("effect:static");
375
+ idiotik.startnow("effect:zoom");
376
+ idiotik.startnow("scream");
377
+ idiotik.startnow("fullscreen");
378
+ idiotik.startnow("freeze");
379
+ idiotik.startnow("cursor:none");
380
+ idiotik.startnow("haunt");
381
+ idiotik.start("say:💀💀💀 YOU ARE SO COOKED 💀💀💀");
382
+ idiotik.repeat("inf");
383
+ },
384
+ mild() {
385
+ idiotik.recursive("no");
386
+ idiotik.volume(100);
387
+ idiotik.startnow("effect:shake");
388
+ idiotik.start("say:gotcha lol");
389
+ idiotik.repeat(5);
390
+ },
391
+ rickroll() {
392
+ idiotik.recursive("no");
393
+ idiotik.startnow("redirect:https://www.youtube.com/watch?v=dQw4w9WgXcQ");
394
+ },
395
+ jumpscare() {
396
+ idiotik.recursive("yes");
397
+ idiotik.volume(300);
398
+ idiotik.wait(3000).then(() => {
399
+ idiotik.startnow("effect:glitch");
400
+ idiotik.startnow("jumpscare:https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/640px-Camponotus_flavomarginatus_ant.jpg");
401
+ idiotik.startnow("scream");
402
+ });
403
+ },
404
+ epilepsy() {
405
+ idiotik.recursive("yes");
406
+ idiotik.volume(450);
407
+ idiotik.startnow("effect:rainbow");
408
+ idiotik.startnow("effect:shake");
409
+ idiotik.startnow("effect:glitch");
410
+ idiotik.startnow("effect:invert");
411
+ idiotik.startnow("effect:static");
412
+ idiotik.startnow("cursor:rainbow");
413
+ idiotik.startnow("scream");
414
+ },
415
+ robux() {
416
+ idiotik.recursive("yes");
417
+ idiotik.volume(200);
418
+ idiotik.startnow("effect:rainbow");
419
+ idiotik.startnow("cursor:explode");
420
+ idiotik.start("say:FREE ROBUX AT PORNHUB.COM!!!");
421
+ idiotik.repeat("inf");
422
+ },
423
+ villain() {
424
+ idiotik.recursive("no");
425
+ idiotik.volume(150);
426
+ idiotik.startnow("effect:matrix");
427
+ idiotik.wait(2000).then(() => idiotik.startnow("effect:glitch"));
428
+ idiotik.wait(4000).then(() => {
429
+ idiotik.volume(500);
430
+ idiotik.startnow("effect:shake");
431
+ idiotik.startnow("scream");
432
+ idiotik.recursive("yes");
433
+ idiotik.start("say:did you really think you were safe 🗿");
434
+ idiotik.repeat("inf");
435
+ });
436
+ },
437
+ melt() {
438
+ idiotik.recursive("no");
439
+ idiotik.volume(100);
440
+ idiotik.startnow("effect:melt");
441
+ idiotik.startnow("effect:spin");
442
+ idiotik.wait(3000).then(() => {
443
+ idiotik.volume(300);
444
+ idiotik.startnow("effect:rainbow");
445
+ idiotik.startnow("scream");
446
+ });
447
+ },
448
+ haunted() {
449
+ idiotik.recursive("yes");
450
+ idiotik.volume(80);
451
+ idiotik.startnow("effect:vhs");
452
+ idiotik.startnow("effect:glitch");
453
+ idiotik.startnow("cursor:ghost");
454
+ idiotik.startnow("haunt");
455
+ idiotik.wait(5000).then(() => {
456
+ idiotik.volume(600);
457
+ idiotik.startnow("scream");
458
+ idiotik.startnow("effect:flip");
459
+ idiotik.startnow("effect:shake");
460
+ idiotik.start("say:👁️ i was here the whole time 👁️");
461
+ idiotik.repeat("inf");
462
+ });
463
+ },
464
+ gravity() {
465
+ idiotik.recursive("yes");
466
+ idiotik.volume(200);
467
+ idiotik.startnow("gravitywarp");
468
+ idiotik.startnow("effect:melt");
469
+ idiotik.startnow("cursor:eyes");
470
+ idiotik.wait(3000).then(() => {
471
+ idiotik.startnow("effect:rainbow");
472
+ idiotik.startnow("scream");
473
+ });
603
474
  },
604
475
  };
605
476
 
@@ -610,7 +481,7 @@
610
481
 
611
482
  recursive(val) {
612
483
  _recursive = (val === true || val === "yes" || val === 1);
613
- if (_recursive) _initRecursion(); // can be toggled on mid-session
484
+ if (_recursive) _initRecursion();
614
485
  return idiotik;
615
486
  },
616
487
 
@@ -628,11 +499,8 @@
628
499
 
629
500
  preset(name) {
630
501
  return function() {
631
- if (_presets[name]) {
632
- _presets[name]();
633
- } else {
634
- console.warn(`idiotik.preset: unknown preset "${name}". run idiotik.list() to see available presets.`);
635
- }
502
+ if (_presets[name]) _presets[name]();
503
+ else console.warn(`idiotik.preset: unknown preset "${name}". try idiotik.list()`);
636
504
  };
637
505
  },
638
506
 
@@ -640,7 +508,7 @@
640
508
  const names = Object.keys(_presets);
641
509
  console.log(`%cidiotik.js — available presets (${names.length})`, "font-weight:bold;color:#ff4444;font-size:14px;");
642
510
  names.forEach(n => console.log(` %c• ${n}`, "color:#ffcc00;"));
643
- console.log('%cusage: idiotik.start(idiotik.preset("name"))()', "color:#aaa;font-style:italic;");
511
+ console.log('%cusage: idiotik.start("preset:name")', "color:#aaa;font-style:italic;");
644
512
  return names;
645
513
  },
646
514
 
@@ -652,23 +520,14 @@
652
520
  // ── SCRIPT CONTEXT ───────────────────────────────────────────────────────
653
521
 
654
522
  script() {
655
- const ctx = {
656
- id: null,
657
- intervals: [],
658
- timeouts: [],
659
- audioSources: [],
660
- htmlNodes: [],
661
- _html: null,
662
- };
663
- _scriptStack.push(ctx);
523
+ _scriptStack.push({ id: null, intervals: [], timeouts: [], audioSources: [], htmlNodes: [], _html: null });
664
524
  return idiotik;
665
525
  },
666
526
 
667
527
  id(name) {
668
528
  _requireScript("id");
669
529
  const ctx = _currentScript();
670
- ctx.id = name;
671
- _activeScripts[name] = ctx;
530
+ ctx.id = name; _activeScripts[name] = ctx;
672
531
  return idiotik;
673
532
  },
674
533
 
@@ -677,15 +536,11 @@
677
536
  const ctx = _currentScript();
678
537
  ctx._html = code;
679
538
  const div = document.createElement("div");
680
- div.innerHTML = code;
681
- document.body.appendChild(div);
682
- ctx.htmlNodes.push(div);
539
+ div.innerHTML = code; document.body.appendChild(div); ctx.htmlNodes.push(div);
683
540
  if (_recursive) {
684
541
  const id = setInterval(() => {
685
- const d = document.createElement("div");
686
- d.innerHTML = code;
687
- document.body.appendChild(d);
688
- ctx.htmlNodes.push(d);
542
+ const d = document.createElement("div"); d.innerHTML = code;
543
+ document.body.appendChild(d); ctx.htmlNodes.push(d);
689
544
  }, 500);
690
545
  ctx.intervals.push(id);
691
546
  }
@@ -698,24 +553,21 @@
698
553
  return idiotik;
699
554
  },
700
555
 
701
- // ── FLOW CONTROL ─────────────────────────────────────────────────────────
556
+ // ── QUEUE / START SYSTEM ─────────────────────────────────────────────────
702
557
 
703
558
  /**
704
- * NEW OVERLOADED start():
559
+ * idiotik.start(ref)
705
560
  *
706
- * idiotik.start() → fires ALL queued tasks
707
- * idiotik.start(n) → fires last n queued tasks (number)
708
- * idiotik.start(fn) → fires fn immediately (original behavior), queues it for batch
561
+ * start() → fire ALL queued tasks, clear queue
562
+ * start(n) → fire task at 1-based position n (don't clear)
563
+ * start("effect:shake") → enqueue effect
564
+ * start("say:hello") → enqueue popup
565
+ * start("preset:robux") → enqueue preset
566
+ * start("scream") → enqueue scream
567
+ * start(fn) → enqueue a raw function
709
568
  */
710
569
  start(ref) {
711
- // number overload — fire last n from queue
712
- if (typeof ref === "number") {
713
- const tasks = _taskQueue.slice(-ref);
714
- tasks.forEach(fn => { try { fn(); } catch(e) {} });
715
- return idiotik;
716
- }
717
-
718
- // no arg — fire ALL queued tasks
570
+ // fire all
719
571
  if (ref === undefined || ref === null) {
720
572
  const tasks = [..._taskQueue];
721
573
  _taskQueue = [];
@@ -723,56 +575,112 @@
723
575
  return idiotik;
724
576
  }
725
577
 
726
- // function — original behavior: run it now AND queue it
727
- if (typeof ref === "function") {
728
- _taskQueue.push(ref);
729
- ref();
578
+ // fire by 1-based index
579
+ if (typeof ref === "number") {
580
+ const idx = ref - 1;
581
+ if (idx >= 0 && idx < _taskQueue.length) {
582
+ try { _taskQueue[idx](); } catch(e) {}
583
+ } else {
584
+ console.warn(`idiotik.start(${ref}): no task at position ${ref} (queue length: ${_taskQueue.length})`);
585
+ }
586
+ return idiotik;
730
587
  }
731
588
 
589
+ // resolve and enqueue
590
+ const fn = _resolve(ref);
591
+ if (fn) _taskQueue.push(fn);
592
+ return idiotik;
593
+ },
594
+
595
+ /**
596
+ * idiotik.startnow(ref)
597
+ * Same DSL as start() but resolves AND fires immediately.
598
+ * Does NOT use the queue.
599
+ */
600
+ startnow(ref) {
601
+ const fn = _resolve(ref);
602
+ if (fn) { try { fn(); } catch(e) {} }
603
+ return idiotik;
604
+ },
605
+
606
+ // ── CLICK BINDING ────────────────────────────────────────────────────────
607
+
608
+ /**
609
+ * idiotik.click(target, ref?)
610
+ * Fires queued tasks (or ref) when a matching element is clicked.
611
+ * target: CSS selector or plain text matched against clickable elements.
612
+ * ref: optional DSL string or function — if omitted, snapshots current queue.
613
+ */
614
+ click(target, ref) {
615
+ const tasksSnapshot = ref ? null : [..._taskQueue];
616
+
617
+ const _fire = function(e) {
618
+ if (ref) {
619
+ const fn = _resolve(ref);
620
+ if (fn) { try { fn(e); } catch(err) {} }
621
+ } else {
622
+ tasksSnapshot.forEach(task => { try { task(e); } catch(err) {} });
623
+ }
624
+ };
625
+
626
+ const _attach = function() {
627
+ let els = [];
628
+ try { els = [...document.querySelectorAll(target)]; } catch(e) {}
629
+ if (!els.length) {
630
+ const lower = target.toLowerCase().trim();
631
+ els = [...document.querySelectorAll("button,a,input[type='button'],input[type='submit'],[role='button'],label,[onclick]")]
632
+ .filter(el => el.textContent.trim().toLowerCase().includes(lower)
633
+ || (el.value && el.value.trim().toLowerCase().includes(lower)));
634
+ }
635
+ if (!els.length) console.warn(`idiotik.click: no elements found for "${target}" — will retry on DOM changes`);
636
+ els.forEach(el => {
637
+ if (el.__idiotikClick) return;
638
+ el.__idiotikClick = true;
639
+ el.addEventListener("click", _fire);
640
+ _track(el, "htmlNodes");
641
+ });
642
+ };
643
+
644
+ if (document.readyState !== "loading") _attach();
645
+ else document.addEventListener("DOMContentLoaded", _attach);
646
+
647
+ const observer = new MutationObserver(_attach);
648
+ observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
649
+ if (!_globalNodes._observers) _globalNodes._observers = [];
650
+ _globalNodes._observers.push(observer);
651
+
732
652
  return idiotik;
733
653
  },
734
654
 
735
- // Queue a task without running it yet (use with idiotik.start())
736
655
  queue(ref) {
737
- if (typeof ref === "function") _taskQueue.push(ref);
656
+ const fn = _resolve(ref);
657
+ if (fn) _taskQueue.push(fn);
738
658
  return idiotik;
739
659
  },
740
660
 
741
- // Clear the task queue without firing
742
661
  clearQueue() {
743
662
  _taskQueue = [];
744
663
  return idiotik;
745
664
  },
746
665
 
747
666
  repeat(n) {
748
- const queue = [..._taskQueue];
749
- if (!queue.length) return idiotik;
750
- const fn = queue[queue.length - 1]; // repeat last queued task
667
+ if (!_taskQueue.length) return idiotik;
668
+ const fn = _taskQueue[_taskQueue.length - 1];
751
669
  const infinite = (n === Infinity || n === "inf" || n === "Infinity");
752
670
  if (infinite) {
753
- const id = setInterval(() => fn(), 100);
754
- _track(id, "intervals");
671
+ _track(setInterval(() => fn(), 100), "intervals");
755
672
  } else {
756
673
  let count = 0;
757
- const id = setInterval(() => {
758
- fn();
759
- count++;
760
- if (count >= n) clearInterval(id);
761
- }, 100);
674
+ const id = setInterval(() => { fn(); count++; if (count >= n) clearInterval(id); }, 100);
762
675
  _track(id, "intervals");
763
676
  }
764
677
  return idiotik;
765
678
  },
766
679
 
767
- get continue() {
768
- return idiotik;
769
- },
680
+ get continue() { return idiotik; },
770
681
 
771
682
  wait(ms) {
772
- return new Promise(resolve => {
773
- const id = setTimeout(resolve, ms);
774
- _track(id, "timeouts");
775
- });
683
+ return new Promise(resolve => { _track(setTimeout(resolve, ms), "timeouts"); });
776
684
  },
777
685
 
778
686
  halt(name) {
@@ -786,7 +694,6 @@
786
694
  return idiotik;
787
695
  },
788
696
 
789
- // NEW: kill literally everything idiotik created
790
697
  nuke() {
791
698
  _recursive = false;
792
699
  _taskQueue = [];
@@ -801,11 +708,14 @@
801
708
  if (_audioCtx) { try { _audioCtx.close(); } catch(e){} _audioCtx = null; }
802
709
  _spawnedWindows.forEach(w => { try { w.close(); } catch(e){} });
803
710
  _spawnedWindows = [];
711
+ if (_globalNodes._observers) {
712
+ _globalNodes._observers.forEach(obs => { try { obs.disconnect(); } catch(e){} });
713
+ _globalNodes._observers = [];
714
+ }
804
715
  console.log("%cidiotik: nuked. RIP.", "color:#ff4444;font-weight:bold;");
805
716
  return idiotik;
806
717
  },
807
718
 
808
- // NEW: log what's currently active
809
719
  status() {
810
720
  console.log("%cidiotik STATUS", "color:#ff4444;font-weight:bold;font-size:14px;");
811
721
  console.log(" recursive:", _recursive);
@@ -862,9 +772,7 @@
862
772
  };
863
773
  },
864
774
 
865
- redirect(url) {
866
- return function() { window.location.href = url; };
867
- },
775
+ redirect(url) { return function() { window.location.href = url; }; },
868
776
 
869
777
  freeze() {
870
778
  return function() {
@@ -890,14 +798,10 @@
890
798
  const div = document.createElement("div");
891
799
  div.style.cssText = "position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:99999;background:#000;display:flex;align-items:center;justify-content:center;";
892
800
  const img = document.createElement("img");
893
- img.src = imgUrl;
894
- img.style.cssText = "max-width:100%;max-height:100%;object-fit:contain;";
895
- div.appendChild(img);
896
- document.body.appendChild(div);
897
- _track(div, "htmlNodes");
801
+ img.src = imgUrl; img.style.cssText = "max-width:100%;max-height:100%;object-fit:contain;";
802
+ div.appendChild(img); document.body.appendChild(div); _track(div, "htmlNodes");
898
803
  if (sfxUrl) _loadAndPlay(_parseAudioUrl(sfxUrl));
899
- const ctx = _currentScript();
900
- if (ctx) ctx.htmlNodes.push(div);
804
+ const ctx = _currentScript(); if (ctx) ctx.htmlNodes.push(div);
901
805
  if (_recursive) setTimeout(() => { div.remove(); _js(); }, 1000);
902
806
  };
903
807
  },
@@ -907,148 +811,106 @@
907
811
  const ctx = _getAudioCtx();
908
812
  const osc = ctx.createOscillator();
909
813
  const gain = ctx.createGain();
910
- osc.type = "sawtooth";
911
- osc.frequency.value = 880;
912
- // NEW: pitch sweep
814
+ osc.type = "sawtooth"; osc.frequency.value = 880;
913
815
  osc.frequency.linearRampToValueAtTime(440, ctx.currentTime + 1);
914
816
  osc.frequency.linearRampToValueAtTime(1760, ctx.currentTime + 2);
915
817
  gain.gain.value = Math.min(_volume / 100, 10);
916
- osc.connect(gain);
917
- gain.connect(ctx.destination);
918
- osc.start();
919
- setTimeout(() => osc.stop(), 2000);
818
+ osc.connect(gain); gain.connect(ctx.destination);
819
+ osc.start(); setTimeout(() => osc.stop(), 2000);
920
820
  if (_recursive) setTimeout(_scream, 2100);
921
821
  };
922
822
  },
923
823
 
924
- // NEW: white noise burst
925
824
  noise(durationSec) {
926
825
  const dur = durationSec || 1;
927
826
  return function _noise() {
928
827
  const source = _whiteNoise(dur);
929
- source.start();
930
- source.stop(_getAudioCtx().currentTime + dur);
828
+ source.start(); source.stop(_getAudioCtx().currentTime + dur);
931
829
  if (_recursive) setTimeout(_noise, dur * 1000 + 100);
932
830
  };
933
831
  },
934
832
 
935
- // NEW: ghost text that appears/disappears at random spots on the page
936
833
  haunt() {
937
834
  const msgs = ["👁️", "i see you", "leave.", "you can't close this", "help", "HA", "why", "🩸", "turn around"];
938
835
  return function _haunt() {
939
836
  const span = document.createElement("div");
940
837
  span.textContent = msgs[Math.floor(Math.random() * msgs.length)];
941
- span.style.cssText = `
942
- position:fixed;
943
- left:${Math.random() * 90}vw;
944
- top:${Math.random() * 90}vh;
945
- font-size:${16 + Math.random() * 40}px;
946
- color:rgba(255,0,0,${0.3 + Math.random() * 0.7});
947
- pointer-events:none;
948
- z-index:99990;
949
- font-family:serif;
950
- font-style:italic;
951
- transition:opacity 1s;
952
- text-shadow: 0 0 10px red;
953
- `;
954
- document.body.appendChild(span);
955
- _track(span, "htmlNodes");
838
+ span.style.cssText = `position:fixed;left:${Math.random()*90}vw;top:${Math.random()*90}vh;font-size:${16+Math.random()*40}px;color:rgba(255,0,0,${0.3+Math.random()*0.7});pointer-events:none;z-index:99990;font-family:serif;font-style:italic;transition:opacity 1s;text-shadow:0 0 10px red;`;
839
+ document.body.appendChild(span); _track(span, "htmlNodes");
956
840
  setTimeout(() => { span.style.opacity = "0"; setTimeout(() => span.remove(), 1000); }, 1500);
957
841
  if (_recursive) setTimeout(_haunt, 800 + Math.random() * 1200);
958
842
  };
959
843
  },
960
844
 
961
- // NEW: fake cursor that chases the real one, slightly offset and delayed
962
845
  fakeCursor() {
963
846
  return function() {
964
847
  const cur = document.createElement("div");
965
848
  cur.textContent = "🖱️";
966
849
  cur.style.cssText = "position:fixed;font-size:20px;pointer-events:none;z-index:999999;transition:left 0.2s,top 0.2s;";
967
- document.body.appendChild(cur);
968
- _track(cur, "htmlNodes");
850
+ document.body.appendChild(cur); _track(cur, "htmlNodes");
969
851
  let mx = 0, my = 0;
970
852
  document.addEventListener("mousemove", e => { mx = e.clientX; my = e.clientY; });
971
- const id = setInterval(() => {
853
+ _track(setInterval(() => {
972
854
  cur.style.left = (mx + 15 + Math.sin(Date.now() / 200) * 10) + "px";
973
855
  cur.style.top = (my + 15 + Math.cos(Date.now() / 200) * 10) + "px";
974
- }, 50);
975
- _track(id, "intervals");
856
+ }, 50), "intervals");
976
857
  };
977
858
  },
978
859
 
979
- // NEW: makes page elements slowly drift off-screen like they're melting away
980
860
  gravityWarp() {
981
861
  return function _gw() {
982
- const els = [...document.querySelectorAll("p,h1,h2,h3,h4,img,div,a,button,span")].filter(e => e.children.length === 0 || e.tagName === "IMG");
983
- els.forEach((el, i) => {
984
- if (el.dataset.idiotikGravity) return;
985
- el.dataset.idiotikGravity = "1";
986
- const origTransform = el.style.transform;
987
- let vy = 0;
988
- const id = setInterval(() => {
989
- vy += 0.5;
990
- const cur = parseFloat(el.dataset.gy || "0");
991
- el.dataset.gy = cur + vy;
992
- el.style.transform = `${origTransform} translateY(${cur}px) rotate(${cur * 0.1}deg)`;
993
- if (cur > window.innerHeight * 1.5) clearInterval(id);
994
- }, 50);
995
- // stagger
996
- // already using setInterval per element, so no extra track needed
997
- });
862
+ [...document.querySelectorAll("p,h1,h2,h3,h4,img,div,a,button,span")]
863
+ .filter(e => e.children.length === 0 || e.tagName === "IMG")
864
+ .forEach(el => {
865
+ if (el.dataset.idiotikGravity) return;
866
+ el.dataset.idiotikGravity = "1";
867
+ const orig = el.style.transform; let vy = 0;
868
+ const id = setInterval(() => {
869
+ vy += 0.5;
870
+ const cur = parseFloat(el.dataset.gy || "0");
871
+ el.dataset.gy = cur + vy;
872
+ el.style.transform = `${orig} translateY(${cur}px) rotate(${cur * 0.1}deg)`;
873
+ if (cur > window.innerHeight * 1.5) clearInterval(id);
874
+ }, 50);
875
+ });
998
876
  if (_recursive) setTimeout(_gw, 5000);
999
877
  };
1000
878
  },
1001
879
 
1002
- // ── AUDIO ────────────────────────────────────────────────────────────────
1003
-
1004
880
  audio(urlOrFile) {
1005
881
  const parsed = _parseAudioUrl(urlOrFile);
1006
882
  return async function _audio() {
1007
883
  const source = await _loadAndPlay(parsed);
1008
- const ctx = _currentScript();
1009
- if (ctx) ctx.audioSources.push(source);
884
+ const ctx = _currentScript(); if (ctx) ctx.audioSources.push(source);
1010
885
  if (_recursive) source.addEventListener?.("ended", _audio);
1011
886
  };
1012
887
  },
1013
888
 
1014
- // ── EFFECT ───────────────────────────────────────────────────────────────
1015
-
1016
889
  effect(name) {
1017
890
  return function() {
1018
- if (_effectImpls[name]) {
1019
- _activeEffects.add(name);
1020
- _effectImpls[name]();
1021
- } else {
1022
- console.warn(`idiotik.effect: unknown effect "${name}"`);
1023
- }
891
+ if (_effectImpls[name]) { _activeEffects.add(name); _effectImpls[name](); }
892
+ else console.warn(`idiotik.effect: unknown effect "${name}"`);
1024
893
  };
1025
894
  },
1026
895
 
1027
896
  cleareffects() {
1028
897
  _activeEffects.clear();
1029
898
  document.querySelectorAll("[id^='__idiotik_']").forEach(el => el.remove());
1030
- // Remove body animations injected by effects
1031
899
  document.body.style.animation = "";
1032
900
  document.body.style.filter = "";
1033
901
  },
1034
902
 
1035
- // ── CURSOR ───────────────────────────────────────────────────────────────
1036
-
1037
903
  cursor(preset) {
1038
904
  return function() {
1039
905
  const val = _cursorPresets[preset];
1040
- if (!val) {
1041
- document.body.style.cursor = `url(${preset}), auto`;
1042
- return;
1043
- }
906
+ if (!val) { document.body.style.cursor = `url(${preset}), auto`; return; }
1044
907
  if (val === "__rainbow__") {
1045
908
  let h = 0;
1046
- const id = setInterval(() => {
909
+ _track(setInterval(() => {
1047
910
  const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><circle cx='10' cy='10' r='10' fill='hsl(${h},100%,50%)'/></svg>`;
1048
911
  document.body.style.cursor = `url("data:image/svg+xml,${encodeURIComponent(svg)}"), auto`;
1049
912
  h = (h + 10) % 360;
1050
- }, 50);
1051
- _track(id, "intervals");
913
+ }, 50), "intervals");
1052
914
  } else if (val === "__wiggle__") {
1053
915
  document.addEventListener("mousemove", () => {
1054
916
  const offset = Math.sin(Date.now() / 100) * 10;
@@ -1060,49 +922,37 @@
1060
922
  const p = document.createElement("div");
1061
923
  p.textContent = ["💥","✨","⭐","🔥"][i % 4];
1062
924
  p.style.cssText = `position:fixed;left:${e.clientX}px;top:${e.clientY}px;font-size:20px;pointer-events:none;z-index:99999;transition:all 0.6s;`;
1063
- document.body.appendChild(p);
1064
- _track(p, "htmlNodes");
925
+ document.body.appendChild(p); _track(p, "htmlNodes");
1065
926
  const angle = (i / 12) * 2 * Math.PI;
1066
- setTimeout(() => {
1067
- p.style.transform = `translate(${Math.cos(angle)*60}px,${Math.sin(angle)*60}px)`;
1068
- p.style.opacity = "0";
1069
- }, 10);
927
+ setTimeout(() => { p.style.transform = `translate(${Math.cos(angle)*60}px,${Math.sin(angle)*60}px)`; p.style.opacity = "0"; }, 10);
1070
928
  setTimeout(() => p.remove(), 700);
1071
929
  }
1072
930
  });
1073
931
  } else if (val === "__ghost__") {
1074
- // NEW: cursor fades in and out, slightly follows with lag
1075
932
  document.body.style.cursor = "none";
1076
933
  const ghost = document.createElement("div");
1077
934
  ghost.textContent = "👻";
1078
935
  ghost.style.cssText = "position:fixed;font-size:24px;pointer-events:none;z-index:999999;transition:left 0.4s ease,top 0.4s ease;";
1079
- document.body.appendChild(ghost);
1080
- _track(ghost, "htmlNodes");
936
+ document.body.appendChild(ghost); _track(ghost, "htmlNodes");
1081
937
  let gx = 0, gy = 0;
1082
938
  document.addEventListener("mousemove", e => { gx = e.clientX; gy = e.clientY; });
1083
- const id = setInterval(() => {
1084
- ghost.style.left = gx + "px";
1085
- ghost.style.top = gy + "px";
939
+ _track(setInterval(() => {
940
+ ghost.style.left = gx + "px"; ghost.style.top = gy + "px";
1086
941
  ghost.style.opacity = (0.5 + 0.5 * Math.sin(Date.now() / 300)).toString();
1087
- }, 50);
1088
- _track(id, "intervals");
942
+ }, 50), "intervals");
1089
943
  } else if (val === "__eyes__") {
1090
- // NEW: two eyes that follow cursor around the page
1091
944
  document.body.style.cursor = "none";
1092
945
  ["left:30vw", "left:60vw"].forEach(pos => {
1093
946
  const eye = document.createElement("div");
1094
947
  eye.style.cssText = `position:fixed;top:50vh;${pos};font-size:48px;pointer-events:none;z-index:999999;transition:transform 0.1s;`;
1095
- eye.textContent = "👁️";
1096
- document.body.appendChild(eye);
1097
- _track(eye, "htmlNodes");
948
+ eye.textContent = "👁️"; document.body.appendChild(eye); _track(eye, "htmlNodes");
1098
949
  });
1099
950
  document.addEventListener("mousemove", e => {
1100
951
  document.querySelectorAll("[style*='👁️']").forEach(el => {
1101
952
  const rect = el.getBoundingClientRect();
1102
953
  const dx = e.clientX - (rect.left + rect.width / 2);
1103
954
  const dy = e.clientY - (rect.top + rect.height / 2);
1104
- const angle = Math.atan2(dy, dx);
1105
- el.style.transform = `rotate(${angle}rad)`;
955
+ el.style.transform = `rotate(${Math.atan2(dy, dx)}rad)`;
1106
956
  });
1107
957
  });
1108
958
  } else {