idiotik.js 1.0.0

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 (3) hide show
  1. package/README.md +196 -0
  2. package/package.json +16 -0
  3. package/src/idiot.js +546 -0
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # idiot.js
2
+
3
+ youareanidiot-style chaos toolkit for the browser. recursive by default. you were warned.
4
+
5
+ ---
6
+
7
+ ## install
8
+
9
+ ```bash
10
+ npm install idiot.js
11
+ ```
12
+
13
+ or just drop `src/idiot.js` into your page:
14
+
15
+ ```html
16
+ <script src="idiot.js"></script>
17
+ ```
18
+
19
+ ---
20
+
21
+ ## api
22
+
23
+ ### global toggles
24
+
25
+ ```js
26
+ idiot.recursive(yes/no) // default: yes — everything spawns itself
27
+ idiot.volume(0-5000) // 101-200: loud | 201-449: + bass boost | 450+: full EQ destruction
28
+ ```
29
+
30
+ ---
31
+
32
+ ### script context
33
+
34
+ group and tag sequences so you can kill them later.
35
+
36
+ ```js
37
+ idiot.script()
38
+ idiot.id("myscript") // name it so idiot.halt() can kill it
39
+ idiot.html("<b>chaos</b>") // inject html — ONLY works inside script()
40
+ idiot.start(idiot.html)
41
+ idiot.repeat(inf)
42
+ idiot.continue // keep running, move on
43
+ idiot.end()
44
+
45
+ idiot.halt("myscript") // kills everything under that id
46
+ ```
47
+
48
+ - `idiot.html()` **must** be inside `idiot.script()` — throws if used outside
49
+ - `idiot.id()` **must** be inside `idiot.script()`
50
+ - everything else works inside or outside
51
+
52
+ ---
53
+
54
+ ### flow control
55
+
56
+ ```js
57
+ idiot.start(ref) // fire a method
58
+ idiot.repeat(n / inf) // repeat last start n times or forever
59
+ idiot.continue // don't block, keep going (async)
60
+ idiot.wait(ms) // delay (returns promise)
61
+ idiot.halt("id") // force stop a named script
62
+ idiot.random(a, b, c) // picks one randomly and fires it
63
+ idiot.if(condition).then(fn).else(fn2)
64
+ ```
65
+
66
+ ---
67
+
68
+ ### chaos methods
69
+
70
+ ```js
71
+ idiot.say("msg") // alert loop
72
+ idiot.scream() // audio blast via oscillator
73
+ idiot.jumpscare(imgUrl, sfxUrl) // fullscreen image + sound
74
+ idiot.redirect(url) // sends em somewhere else
75
+ idiot.freeze() // disables all user input
76
+ idiot.fullscreen() // forces fullscreen, fights exit
77
+ ```
78
+
79
+ ---
80
+
81
+ ### audio
82
+
83
+ ```js
84
+ idiot.audio("https://example.com/sound.mp3") // from url
85
+ idiot.audio("file://mysound.mp3") // local file (strips file://)
86
+ idiot.volume(150) // loud
87
+ idiot.volume(300) // loud + bass boosted
88
+ idiot.volume(500) // loud + bass boost + every EQ band maxed 💀
89
+ idiot.volume(5000) // sonic weapon
90
+ ```
91
+
92
+ ---
93
+
94
+ ### effects (stackable)
95
+
96
+ ```js
97
+ idiot.start(idiot.effect("shake"))() // seizure mode
98
+ idiot.start(idiot.effect("spin"))() // rotating hell
99
+ idiot.start(idiot.effect("glitch"))() // css glitch
100
+ idiot.start(idiot.effect("rainbow"))() // color flash
101
+ idiot.start(idiot.effect("invert"))() // inverts page
102
+ idiot.start(idiot.effect("matrix"))() // you already know
103
+ idiot.start(idiot.effect("melt"))() // page droops
104
+
105
+ idiot.cleareffects() // wipe all active effects
106
+ ```
107
+
108
+ effects stack — calling multiple adds them all simultaneously.
109
+
110
+ ---
111
+
112
+ ### cursor
113
+
114
+ ```js
115
+ idiot.start(idiot.cursor("hand")) // pointer everywhere
116
+ idiot.start(idiot.cursor("none")) // cursor gone
117
+ idiot.start(idiot.cursor("rainbow")) // color trail
118
+ idiot.start(idiot.cursor("wiggle")) // cursor fights you
119
+ idiot.start(idiot.cursor("explode")) // particles on click
120
+ idiot.start(idiot.cursor("crosshair")) // crosshair
121
+ idiot.start(idiot.cursor("https://example.com/cur.png")) // custom url
122
+ ```
123
+
124
+ ---
125
+
126
+ ### presets
127
+
128
+ ```js
129
+ idiot.start(idiot.classic())() // the full youareanidiot experience
130
+ idiot.start(idiot.mild())() // annoying but survivable
131
+ idiot.start(idiot.nuclear())() // god help them
132
+ idiot.start(idiot.rickroll())() // you know what this does
133
+ ```
134
+
135
+ ---
136
+
137
+ ## examples
138
+
139
+ ### basic alert spam
140
+
141
+ ```js
142
+ idiot.start(idiot.say("FREE ROBUX AT PORNHUB.COM!!!"))
143
+ idiot.repeat(inf)
144
+ idiot.continue
145
+ ```
146
+
147
+ ### named script you can kill
148
+
149
+ ```js
150
+ idiot.script()
151
+ idiot.id("chaos")
152
+ idiot.say("YOU ARE AN IDIOT")
153
+ idiot.start(idiot.say)
154
+ idiot.repeat(inf)
155
+ idiot.continue
156
+ idiot.end()
157
+
158
+ // somewhere else:
159
+ idiot.halt("chaos")
160
+ ```
161
+
162
+ ### stacked effects
163
+
164
+ ```js
165
+ idiot.recursive("yes")
166
+ idiot.volume(5000)
167
+ idiot.start(idiot.effect("shake"))()
168
+ idiot.start(idiot.effect("rainbow"))()
169
+ idiot.start(idiot.effect("glitch"))()
170
+ idiot.start(idiot.scream())()
171
+ ```
172
+
173
+ ### conditional chaos
174
+
175
+ ```js
176
+ idiot.if(userIsAnnoying).then(() => {
177
+ idiot.start(idiot.nuclear())()
178
+ }).else(() => {
179
+ idiot.start(idiot.mild())()
180
+ })
181
+ ```
182
+
183
+ ---
184
+
185
+ ## volume breakdown
186
+
187
+ | range | behavior |
188
+ |-------|----------|
189
+ | 0–100 | normal |
190
+ | 101–200 | just loud |
191
+ | 201–449 | loud + bass boost (scales with value) |
192
+ | 450–5000 | loud + bass boost + every EQ band maxed |
193
+
194
+ ---
195
+
196
+ built different. use responsibly. (don't.)
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "idiotik.js",
3
+ "version": "1.0.0",
4
+ "description": "youareanidiot-style chaos toolkit for the browser",
5
+ "main": "src/idiot.js",
6
+ "scripts": {
7
+ "build": "node build.js"
8
+ },
9
+ "keywords": ["chaos", "prank", "browser", "youareanidiot"],
10
+ "license": "MIT",
11
+ "files": [
12
+ "src/",
13
+ "presets/",
14
+ "README.md"
15
+ ]
16
+ }
package/src/idiot.js ADDED
@@ -0,0 +1,546 @@
1
+ /**
2
+ * idiot.js — youareanidiot-style chaos toolkit
3
+ * recursive by default. you were warned.
4
+ */
5
+
6
+ (function (global) {
7
+
8
+ // ─── GLOBAL STATE ───────────────────────────────────────────────────────────
9
+ let _recursive = true;
10
+ let _volume = 100;
11
+ let _activeScripts = {}; // id -> { intervals, timeouts, audioNodes }
12
+ let _activeEffects = new Set();
13
+ let _audioCtx = null;
14
+ let _lastStarted = null; // last method ref passed to idiot.start()
15
+ let _scriptStack = []; // current script context stack
16
+
17
+ // ─── HELPERS ────────────────────────────────────────────────────────────────
18
+ function _getAudioCtx() {
19
+ if (!_audioCtx) _audioCtx = new (window.AudioContext || window.webkitAudioContext)();
20
+ return _audioCtx;
21
+ }
22
+
23
+ function _currentScript() {
24
+ return _scriptStack.length ? _scriptStack[_scriptStack.length - 1] : null;
25
+ }
26
+
27
+ function _requireScript(name) {
28
+ if (!_currentScript()) throw new Error(`idiot.${name}() must be inside idiot.script()`);
29
+ }
30
+
31
+ function _track(thing, type) {
32
+ const ctx = _currentScript();
33
+ if (!ctx) return;
34
+ ctx[type] = ctx[type] || [];
35
+ ctx[type].push(thing);
36
+ }
37
+
38
+ function _buildAudioChain(buffer) {
39
+ const ctx = _getAudioCtx();
40
+ const source = ctx.createBufferSource();
41
+ source.buffer = buffer;
42
+ source.loop = true;
43
+
44
+ const gainNode = ctx.createGain();
45
+ const rawVol = _volume;
46
+
47
+ // volume scaling
48
+ if (rawVol <= 100) {
49
+ gainNode.gain.value = rawVol / 100;
50
+ } else if (rawVol <= 200) {
51
+ gainNode.gain.value = rawVol / 100;
52
+ } else {
53
+ gainNode.gain.value = Math.min(rawVol / 100, 50);
54
+ }
55
+
56
+ let lastNode = source;
57
+
58
+ // bass boost kicks in at 201
59
+ if (rawVol > 200) {
60
+ const bassBoost = ctx.createBiquadFilter();
61
+ bassBoost.type = "lowshelf";
62
+ bassBoost.frequency.value = 200;
63
+ const boostAmount = Math.min(((rawVol - 200) / 4800) * 40, 40);
64
+ bassBoost.gain.value = boostAmount;
65
+ lastNode.connect(bassBoost);
66
+ lastNode = bassBoost;
67
+ }
68
+
69
+ // full EQ rape kicks in at 450
70
+ if (rawVol >= 450) {
71
+ const bands = [
72
+ { type: "lowshelf", freq: 60, gain: 40 },
73
+ { type: "peaking", freq: 250, gain: 40 },
74
+ { type: "peaking", freq: 500, gain: 40 },
75
+ { type: "peaking", freq: 1500, gain: 40 },
76
+ { type: "peaking", freq: 4000, gain: 40 },
77
+ { type: "peaking", freq: 8000, gain: 40 },
78
+ { type: "highshelf", freq: 16000, gain: 40 },
79
+ ];
80
+ for (const b of bands) {
81
+ const f = ctx.createBiquadFilter();
82
+ f.type = b.type;
83
+ f.frequency.value = b.freq;
84
+ f.gain.value = b.gain;
85
+ lastNode.connect(f);
86
+ lastNode = f;
87
+ }
88
+ }
89
+
90
+ lastNode.connect(gainNode);
91
+ gainNode.connect(ctx.destination);
92
+ return source;
93
+ }
94
+
95
+ async function _loadAndPlay(url) {
96
+ const ctx = _getAudioCtx();
97
+ const resp = await fetch(url);
98
+ const arrayBuf = await resp.arrayBuffer();
99
+ const audioBuf = await ctx.decodeAudioData(arrayBuf);
100
+ const source = _buildAudioChain(audioBuf);
101
+ source.start(0);
102
+ return source;
103
+ }
104
+
105
+ // ─── CURSOR PRESETS ─────────────────────────────────────────────────────────
106
+ const _cursorPresets = {
107
+ hand: "pointer",
108
+ none: "none",
109
+ crosshair: "crosshair",
110
+ wait: "wait",
111
+ // animated ones inject a style tag
112
+ rainbow: "__rainbow__",
113
+ wiggle: "__wiggle__",
114
+ explode: "__explode__",
115
+ };
116
+
117
+ // ─── EFFECT IMPLEMENTATIONS ─────────────────────────────────────────────────
118
+ const _effectImpls = {
119
+ shake() {
120
+ const styleId = "__idiot_shake__";
121
+ if (document.getElementById(styleId)) return;
122
+ const s = document.createElement("style");
123
+ s.id = styleId;
124
+ s.textContent = `
125
+ @keyframes __idiot_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)} }
126
+ body { animation: __idiot_shake 0.3s infinite; }
127
+ `;
128
+ document.head.appendChild(s);
129
+ },
130
+ spin() {
131
+ const styleId = "__idiot_spin__";
132
+ if (document.getElementById(styleId)) return;
133
+ const s = document.createElement("style");
134
+ s.id = styleId;
135
+ s.textContent = `
136
+ @keyframes __idiot_spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
137
+ body { animation: __idiot_spin 2s linear infinite; transform-origin: center center; }
138
+ `;
139
+ document.head.appendChild(s);
140
+ },
141
+ glitch() {
142
+ const styleId = "__idiot_glitch__";
143
+ if (document.getElementById(styleId)) return;
144
+ const s = document.createElement("style");
145
+ s.id = styleId;
146
+ s.textContent = `
147
+ @keyframes __idiot_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)} }
148
+ @keyframes __idiot_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)} }
149
+ body::before, body::after { content:''; position:fixed; top:0;left:0;width:100%;height:100%; background:inherit; pointer-events:none; z-index:9999; }
150
+ body::before { animation: __idiot_glitch1 0.4s infinite; color:red; text-shadow:2px 0 red; }
151
+ body::after { animation: __idiot_glitch2 0.4s infinite; color:blue; text-shadow:-2px 0 blue; }
152
+ `;
153
+ document.head.appendChild(s);
154
+ },
155
+ rainbow() {
156
+ const styleId = "__idiot_rainbow__";
157
+ if (document.getElementById(styleId)) return;
158
+ const s = document.createElement("style");
159
+ s.id = styleId;
160
+ s.textContent = `
161
+ @keyframes __idiot_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} }
162
+ body { animation: __idiot_rainbow 0.5s infinite; }
163
+ `;
164
+ document.head.appendChild(s);
165
+ },
166
+ invert() {
167
+ const styleId = "__idiot_invert__";
168
+ if (document.getElementById(styleId)) return;
169
+ const s = document.createElement("style");
170
+ s.id = styleId;
171
+ s.textContent = `body { filter: invert(1); }`;
172
+ document.head.appendChild(s);
173
+ },
174
+ matrix() {
175
+ const canvasId = "__idiot_matrix__";
176
+ if (document.getElementById(canvasId)) return;
177
+ const canvas = document.createElement("canvas");
178
+ canvas.id = canvasId;
179
+ canvas.style.cssText = "position:fixed;top:0;left:0;z-index:9998;pointer-events:none;width:100vw;height:100vh;opacity:0.7;";
180
+ document.body.appendChild(canvas);
181
+ const ctx = canvas.getContext("2d");
182
+ canvas.width = window.innerWidth;
183
+ canvas.height = window.innerHeight;
184
+ const cols = Math.floor(canvas.width / 16);
185
+ const drops = Array(cols).fill(1);
186
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#$%^&*()";
187
+ const id = setInterval(() => {
188
+ ctx.fillStyle = "rgba(0,0,0,0.05)";
189
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
190
+ ctx.fillStyle = "#0f0";
191
+ ctx.font = "16px monospace";
192
+ drops.forEach((y, i) => {
193
+ const ch = chars[Math.floor(Math.random() * chars.length)];
194
+ ctx.fillText(ch, i * 16, y * 16);
195
+ if (y * 16 > canvas.height && Math.random() > 0.975) drops[i] = 0;
196
+ drops[i]++;
197
+ });
198
+ }, 50);
199
+ _track(id, "intervals");
200
+ },
201
+ melt() {
202
+ const styleId = "__idiot_melt__";
203
+ if (document.getElementById(styleId)) return;
204
+ const s = document.createElement("style");
205
+ s.id = styleId;
206
+ s.textContent = `
207
+ @keyframes __idiot_melt { 0%{transform:skewY(0deg) scaleY(1)} 50%{transform:skewY(5deg) scaleY(1.1)} 100%{transform:skewY(-3deg) scaleY(0.9)} }
208
+ body { animation: __idiot_melt 1s ease-in-out infinite alternate; transform-origin: bottom; }
209
+ `;
210
+ document.head.appendChild(s);
211
+ },
212
+ };
213
+
214
+ // ─── CORE idiot OBJECT ───────────────────────────────────────────────────────
215
+ const idiot = {
216
+
217
+ // ── GLOBAL TOGGLES ─────────────────────────────────────────────────────────
218
+
219
+ recursive(val) {
220
+ _recursive = (val === true || val === "yes" || val === 1);
221
+ return idiot;
222
+ },
223
+
224
+ volume(n) {
225
+ _volume = Math.max(0, Math.min(5000, Number(n)));
226
+ return idiot;
227
+ },
228
+
229
+ // ── SCRIPT CONTEXT ─────────────────────────────────────────────────────────
230
+
231
+ script() {
232
+ const ctx = {
233
+ id: null,
234
+ intervals: [],
235
+ timeouts: [],
236
+ audioSources: [],
237
+ htmlNodes: [],
238
+ _html: null,
239
+ };
240
+ _scriptStack.push(ctx);
241
+ return idiot;
242
+ },
243
+
244
+ id(name) {
245
+ _requireScript("id");
246
+ const ctx = _currentScript();
247
+ ctx.id = name;
248
+ _activeScripts[name] = ctx;
249
+ return idiot;
250
+ },
251
+
252
+ html(code) {
253
+ _requireScript("html");
254
+ const ctx = _currentScript();
255
+ ctx._html = code;
256
+ return idiot;
257
+ },
258
+
259
+ end() {
260
+ if (!_currentScript()) throw new Error("idiot.end() called outside a script");
261
+ _scriptStack.pop();
262
+ return idiot;
263
+ },
264
+
265
+ // ── FLOW CONTROL ───────────────────────────────────────────────────────────
266
+
267
+ start(ref) {
268
+ _lastStarted = ref;
269
+ if (typeof ref === "function") {
270
+ ref();
271
+ }
272
+ return idiot;
273
+ },
274
+
275
+ repeat(n) {
276
+ if (!_lastStarted) return idiot;
277
+ const fn = _lastStarted;
278
+ const infinite = (n === Infinity || n === "inf" || n === "Infinity");
279
+ if (infinite) {
280
+ const id = setInterval(() => fn(), 100);
281
+ _track(id, "intervals");
282
+ } else {
283
+ let count = 0;
284
+ const id = setInterval(() => {
285
+ fn();
286
+ count++;
287
+ if (count >= n) clearInterval(id);
288
+ }, 100);
289
+ _track(id, "intervals");
290
+ }
291
+ return idiot;
292
+ },
293
+
294
+ get continue() {
295
+ // no-op — execution continues naturally (async stuff already running)
296
+ return idiot;
297
+ },
298
+
299
+ wait(ms) {
300
+ return new Promise(resolve => {
301
+ const id = setTimeout(resolve, ms);
302
+ _track(id, "timeouts");
303
+ });
304
+ },
305
+
306
+ halt(name) {
307
+ const ctx = _activeScripts[name];
308
+ if (!ctx) return idiot;
309
+ (ctx.intervals || []).forEach(id => clearInterval(id));
310
+ (ctx.timeouts || []).forEach(id => clearTimeout(id));
311
+ (ctx.audioSources || []).forEach(s => { try { s.stop(); } catch(e){} });
312
+ (ctx.htmlNodes || []).forEach(n => { try { n.remove(); } catch(e){} });
313
+ delete _activeScripts[name];
314
+ return idiot;
315
+ },
316
+
317
+ // ── CONDITIONAL ────────────────────────────────────────────────────────────
318
+
319
+ _ifResult: null,
320
+
321
+ if(condition) {
322
+ idiot._ifResult = !!condition;
323
+ return {
324
+ then(fn) {
325
+ if (idiot._ifResult) fn();
326
+ return {
327
+ else(fn2) { if (!idiot._ifResult) fn2(); return idiot; }
328
+ };
329
+ }
330
+ };
331
+ },
332
+
333
+ else(fn) {
334
+ if (!idiot._ifResult) fn();
335
+ return idiot;
336
+ },
337
+
338
+ random(...methods) {
339
+ const pick = methods[Math.floor(Math.random() * methods.length)];
340
+ if (typeof pick === "function") pick();
341
+ return idiot;
342
+ },
343
+
344
+ // ── CHAOS METHODS ──────────────────────────────────────────────────────────
345
+
346
+ say(msg) {
347
+ return function _say() {
348
+ alert(msg);
349
+ if (_recursive) setTimeout(() => _say(), 0);
350
+ };
351
+ },
352
+
353
+ redirect(url) {
354
+ return function() {
355
+ window.location.href = url;
356
+ };
357
+ },
358
+
359
+ freeze() {
360
+ return function() {
361
+ document.addEventListener("keydown", e => e.preventDefault(), true);
362
+ document.addEventListener("mousedown", e => e.preventDefault(), true);
363
+ document.addEventListener("contextmenu", e => e.preventDefault(), true);
364
+ };
365
+ },
366
+
367
+ fullscreen() {
368
+ return function _fs() {
369
+ const el = document.documentElement;
370
+ if (el.requestFullscreen) el.requestFullscreen();
371
+ else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen();
372
+ document.addEventListener("fullscreenchange", () => {
373
+ if (!document.fullscreenElement && _recursive) setTimeout(_fs, 100);
374
+ });
375
+ };
376
+ },
377
+
378
+ jumpscare(imgUrl, sfxUrl) {
379
+ return function _js() {
380
+ const div = document.createElement("div");
381
+ 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;";
382
+ const img = document.createElement("img");
383
+ img.src = imgUrl;
384
+ img.style.cssText = "max-width:100%;max-height:100%;object-fit:contain;";
385
+ div.appendChild(img);
386
+ document.body.appendChild(div);
387
+ if (sfxUrl) {
388
+ const parsed = _parseAudioUrl(sfxUrl);
389
+ _loadAndPlay(parsed);
390
+ }
391
+ const ctx = _currentScript();
392
+ if (ctx) ctx.htmlNodes.push(div);
393
+ if (_recursive) setTimeout(() => { div.remove(); _js(); }, 1000);
394
+ };
395
+ },
396
+
397
+ scream() {
398
+ return function() {
399
+ const ctx = _getAudioCtx();
400
+ const osc = ctx.createOscillator();
401
+ const gain = ctx.createGain();
402
+ osc.type = "sawtooth";
403
+ osc.frequency.value = 880;
404
+ gain.gain.value = Math.min(_volume / 100, 10);
405
+ osc.connect(gain);
406
+ gain.connect(ctx.destination);
407
+ osc.start();
408
+ setTimeout(() => osc.stop(), 2000);
409
+ if (_recursive) setTimeout(idiot.scream()(), 2100);
410
+ };
411
+ },
412
+
413
+ // ── AUDIO ──────────────────────────────────────────────────────────────────
414
+
415
+ audio(urlOrFile) {
416
+ const parsed = _parseAudioUrl(urlOrFile);
417
+ return async function _audio() {
418
+ const source = await _loadAndPlay(parsed);
419
+ const ctx = _currentScript();
420
+ if (ctx) ctx.audioSources.push(source);
421
+ if (_recursive) source.addEventListener?.("ended", _audio);
422
+ };
423
+ },
424
+
425
+ // ── EFFECT ─────────────────────────────────────────────────────────────────
426
+
427
+ effect(name) {
428
+ return function() {
429
+ if (_effectImpls[name]) {
430
+ _activeEffects.add(name);
431
+ _effectImpls[name]();
432
+ } else {
433
+ console.warn(`idiot.effect: unknown preset "${name}"`);
434
+ }
435
+ };
436
+ },
437
+
438
+ cleareffects() {
439
+ _activeEffects.clear();
440
+ document.querySelectorAll("[id^='__idiot_']").forEach(el => el.remove());
441
+ },
442
+
443
+ // ── CURSOR ─────────────────────────────────────────────────────────────────
444
+
445
+ cursor(preset) {
446
+ return function() {
447
+ const val = _cursorPresets[preset];
448
+ if (!val) {
449
+ // treat as URL
450
+ document.body.style.cursor = `url(${preset}), auto`;
451
+ return;
452
+ }
453
+ if (val === "__rainbow__") {
454
+ let h = 0;
455
+ setInterval(() => {
456
+ 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>`;
457
+ const url = `url("data:image/svg+xml,${encodeURIComponent(svg)}")`;
458
+ document.body.style.cursor = `${url}, auto`;
459
+ h = (h + 10) % 360;
460
+ }, 50);
461
+ } else if (val === "__wiggle__") {
462
+ document.addEventListener("mousemove", e => {
463
+ const offset = Math.sin(Date.now() / 100) * 10;
464
+ document.body.style.cursor = `url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><text y='16' font-size='16'>👆</text></svg>") ${offset} 0, auto`;
465
+ });
466
+ } else if (val === "__explode__") {
467
+ document.addEventListener("click", e => {
468
+ for (let i = 0; i < 12; i++) {
469
+ const p = document.createElement("div");
470
+ p.textContent = ["💥","✨","⭐","🔥"][i % 4];
471
+ 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;`;
472
+ document.body.appendChild(p);
473
+ const angle = (i / 12) * 2 * Math.PI;
474
+ setTimeout(() => {
475
+ p.style.transform = `translate(${Math.cos(angle)*60}px,${Math.sin(angle)*60}px)`;
476
+ p.style.opacity = "0";
477
+ }, 10);
478
+ setTimeout(() => p.remove(), 700);
479
+ }
480
+ });
481
+ } else {
482
+ document.body.style.cursor = val;
483
+ }
484
+ };
485
+ },
486
+
487
+ // ── PRESETS ────────────────────────────────────────────────────────────────
488
+
489
+ classic() {
490
+ return function() {
491
+ idiot.recursive("yes");
492
+ idiot.start(idiot.say("YOU ARE AN IDIOT HA HA HA HA HA HA"));
493
+ idiot.start(idiot.effect("rainbow"))();
494
+ idiot.start(idiot.effect("shake"))();
495
+ idiot.start(idiot.scream())();
496
+ };
497
+ },
498
+
499
+ mild() {
500
+ return function() {
501
+ idiot.recursive("no");
502
+ idiot.start(idiot.say("gotcha lol"));
503
+ idiot.start(idiot.effect("shake"))();
504
+ };
505
+ },
506
+
507
+ nuclear() {
508
+ return function() {
509
+ idiot.recursive("yes");
510
+ idiot.volume(5000);
511
+ idiot.start(idiot.effect("shake"))();
512
+ idiot.start(idiot.effect("rainbow"))();
513
+ idiot.start(idiot.effect("glitch"))();
514
+ idiot.start(idiot.effect("spin"))();
515
+ idiot.start(idiot.scream())();
516
+ idiot.start(idiot.fullscreen())();
517
+ idiot.start(idiot.freeze())();
518
+ idiot.start(idiot.say("💀💀💀 YOU ARE SO COOKED 💀💀💀"));
519
+ };
520
+ },
521
+
522
+ rickroll() {
523
+ return function() {
524
+ idiot.recursive("no");
525
+ idiot.start(idiot.redirect("https://www.youtube.com/watch?v=dQw4w9WgXcQ"))();
526
+ };
527
+ },
528
+
529
+ };
530
+
531
+ // ─── INTERNAL UTILS ─────────────────────────────────────────────────────────
532
+ function _parseAudioUrl(urlOrFile) {
533
+ if (typeof urlOrFile === "string" && urlOrFile.startsWith("file://")) {
534
+ return urlOrFile.replace("file://", "");
535
+ }
536
+ return urlOrFile;
537
+ }
538
+
539
+ // ─── EXPORT ─────────────────────────────────────────────────────────────────
540
+ if (typeof module !== "undefined" && module.exports) {
541
+ module.exports = idiot;
542
+ } else {
543
+ global.idiot = idiot;
544
+ }
545
+
546
+ })(typeof window !== "undefined" ? window : global);