Code Snippets

A Collection of Curated Code

Here are some of my favorite JavaScript snippets. They range from highly useful utilities to fun, creative, and sometimes troll-worthy experiments. Many include a live preview below the code.

1. Matrix Digital Rain

A classic effect that simulates the "digital rain" from The Matrix using an HTML Canvas. It's a great example of rendering loops and basic animation.

// HTML Required: 
const canvas = document.getElementById('matrixCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const katakana = 'アァカサタナハマヤャラワガザダバパイィキシチニヒミリヰギジヂビピウゥクスツヌフムユュルグズブヅプエェケセテネヘメレヱゲゼデベペオォコソトノホモヨョロヲゴゾドボポヴッン';
const alphabet = katakana + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const fontSize = 16;
const columns = canvas.width / fontSize;
const rainDrops = [];
for (let x = 0; x < columns; x++) { rainDrops[x] = 1; }
const draw = () => {
    ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#0F0';
    ctx.font = fontSize + 'px monospace';
    for (let i = 0; i < rainDrops.length; i++) {
        const text = alphabet.charAt(Math.floor(Math.random() * alphabet.length));
        ctx.fillText(text, i * fontSize, rainDrops[i] * fontSize);
        if (rainDrops[i] * fontSize > canvas.height && Math.random() > 0.975) { rainDrops[i] = 0; }
        rainDrops[i]++;
    }
};
setInterval(draw, 30);

Live Preview

2. Advanced Debounce Function

Extremely useful for performance. This higher-order function prevents a function from being called too frequently, such as on window resize or search input.

const debounce = (func, wait) => {
    let timeoutId = null;
    return function(...args) {
        const context = this;
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            func.apply(context, args);
        }, wait);
    };
};
// --- Example Usage ---
const searchInput = document.getElementById('searchInput');
const log = document.getElementById('log');
const handleSearch = (event) => {
    log.textContent = `API call with: "${event.target.value}"`;
};
const debouncedSearch = debounce(handleSearch, 500);
searchInput.addEventListener('input', debouncedSearch);

Live Preview

3. Console Log an Image

A very neat trick to display a full-color image directly in the browser's developer console. It works by applying CSS to the console output using the `%c` format specifier.

const logImage = (url, scale = 1) => {
    const img = new Image();
    img.onload = function() {
        const style = [
            'font-size: 1px;',
            `padding: ${this.height * scale / 2}px ${this.width * scale / 2}px;`,
            `background: url(${url}) no-repeat;`,
            'background-size: contain;'
        ].join(' ');
        console.log('%c ', style);
        console.log(`Image logged from: ${url}`);
    };
    img.onerror = () => console.error(`Failed to load image: ${url}`);
    img.src = url;
};
// --- Example Usage ---
logImage('https://i.imgur.com/gY5mO0d.png', 0.5);

Live Preview

Preview not applicable.
Open your browser's developer console (F12) to see the result of this code.

4. Looping Typewriter Effect

This function creates a classic "typewriter" effect that reveals text one character at a time. This version automatically restarts after a 10-second delay.

function typewriter(element, text, speed = 50, onComplete) {
    let i = 0;
    element.innerHTML = "";
    element.style.visibility = 'visible';
    const typing = () => {
        if (i < text.length) {
            element.innerHTML += text.charAt(i);
            i++;
            setTimeout(typing, speed);
        } else if (onComplete) {
            onComplete();
        }
    };
    typing();
}
// --- Example Usage ---
const title = document.getElementById('myTitle');
const text = "Welcome to Shane Studios.\nInitializing...";
const startTypingLoop = () => {
    typewriter(title, text, 75, () => {
        setTimeout(startTypingLoop, 10000); // Wait 10s and restart
    });
};
startTypingLoop();

Live Preview

5. Detect Ad Blocker

A slightly controversial but useful snippet. It works by creating a "bait" element that ad blockers typically hide, then checks if the element is actually visible in the DOM.

async function detectAdBlock() {
    return new Promise((resolve) => {
        const bait = document.createElement('div');
        bait.innerHTML = ' ';
        bait.className = 'pub_300x250 pub_300x250m pub_728x90 text-ad text-ads';
        bait.style.cssText = 'position:absolute; top:-10px; left:-10px; width:1px; height:1px;';
        document.body.appendChild(bait);
        requestAnimationFrame(() => {
            setTimeout(() => {
                resolve(bait.offsetHeight === 0);
                document.body.removeChild(bait);
            }, 100);
        });
    });
}
// --- Example Usage ---
const statusEl = document.getElementById('adblock-status');
detectAdBlock().then(isBlocked => {
    statusEl.textContent = isBlocked ? 'Ad Blocker: DETECTED' : 'Ad Blocker: NOT DETECTED';
    statusEl.style.color = isBlocked ? '#f87171' : '#4ade80';
});

Live Preview

6. Konami Code Listener

A classic easter egg. This snippet listens for the famous Konami Code sequence (↑↑↓↓←→←→BA) and executes a callback function when the user correctly enters it.

function onKonamiCode(callback) {
    const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a'];
    let index = 0;
    document.addEventListener('keydown', (event) => {
        if (event.key === konamiCode[index]) {
            index++;
            if (index === konamiCode.length) {
                callback();
                index = 0;
            }
        } else {
            index = 0;
        }
    });
}
// --- Example Usage ---
onKonamiCode(() => {
    alert('Konami Code Activated! +30 Lives!');
});

Live Preview

7. Deep Clone an Object

A must-have utility. This function recursively clones an object, ensuring that nested objects and arrays are also copied, not just referenced.

function deepClone(source) {
    if (source === null || typeof source !== 'object') return source;
    if (source instanceof Date) return new Date(source.getTime());
    if (source instanceof Array) return source.map(item => deepClone(item));
    if (source instanceof Object) {
        const clone = {};
        for (const key in source) {
            if (source.hasOwnProperty(key)) {
                clone[key] = deepClone(source[key]);
            }
        }
        return clone;
    }
    throw new Error("Unable to copy source!");
}
// --- Example Usage ---
const original = { a: 1, b: { c: [1, 2] } };
const copy = deepClone(original);
copy.b.c.push(3);
console.log('Original:', JSON.stringify(original));
console.log('Copy:', JSON.stringify(copy));

Live Preview

Preview not applicable.
This is a utility function whose result is seen in the developer console.

8. Chaotic Button

A classic troll. This function makes an element jump to a random position within the viewport whenever the mouse cursor gets near it, making it impossible to click.

function makeButtonChaotic(element) {
    const moveButton = () => {
        const w = window.innerWidth, h = window.innerHeight;
        const btnW = element.offsetWidth, btnH = element.offsetHeight;
        const newX = Math.floor(Math.random() * (w - btnW));
        const newY = Math.floor(Math.random() * (h - btnH));
        element.style.left = `${newX}px`;
        element.style.top = `${newY}px`;
    };
    element.addEventListener('mouseover', moveButton);
    element.addEventListener('focus', moveButton);
}
// --- CSS & HTML Required ---
// #chaoticBtn { position: absolute; transition: all 0.2s; }
// 
makeButtonChaotic(document.getElementById('chaoticBtn'));

Live Preview

9. Get Average Color of Image

A very cool utility that uses a canvas to read the pixel data of an image and calculate its average color. Useful for creating dynamic UI that matches image content.

function getAverageRGB(imgEl) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const blockSize = 5; // Process every 5th pixel for speed
    let rgb = { r: 0, g: 0, b: 0 };
    let count = 0;
    canvas.width = imgEl.width;
    canvas.height = imgEl.height;
    ctx.drawImage(imgEl, 0, 0);
    const data = ctx.getImageData(0, 0, canvas.width, canvas.height);
    for (let i = 0; i < data.data.length; i += 4 * blockSize) {
        count++;
        rgb.r += data.data[i];
        rgb.g += data.data[i + 1];
        rgb.b += data.data[i + 2];
    }
    rgb.r = ~~(rgb.r / count);
    rgb.g = ~~(rgb.g / count);
    rgb.b = ~~(rgb.b / count);
    return rgb;
}

Live Preview

This snippet requires an image element and is best used in a larger application. The real power is in the returned RGB object.

10. Smooth Scroll to Element

A modern, one-line replacement for older, more complex scrolling libraries. It uses the built-in `scrollIntoView` method with the 'smooth' behavior option.

const smoothScrollTo = (selector) => {
    document.querySelector(selector)?.scrollIntoView({
        behavior: 'smooth'
    });
};
// --- Example Usage ---
// Go to Target

Live Preview

11. Fake Virus Prank

A harmless but hilarious prank. This snippet simulates a fake virus scan by rapidly printing "scary" messages to the console and changing the page's background color.

function fakeVirus() {
    const messages = [
        '[SCANNING] C:/Windows/System32...',
        '[WARNING] Malware detected: "Win32.Troll.js"',
        '[ACCESSING] Kernel memory...',
        '[ERROR] Permission denied. Overriding...',
        '[UPLOADING] User data to 127.0.0.1...',
        '[SUCCESS] Data compromised.',
        '[DELETING] System files... do not reboot.',
    ];
    let i = 0;
    console.clear();
    const interval = setInterval(() => {
        if (i < messages.length) {
            console.warn(messages[i]);
            i++;
            document.body.style.backgroundColor = i % 2 === 0 ? '#330000' : '#000';
        } else {
            clearInterval(interval);
            console.error('[FATAL] System integrity compromised. Just kidding!');
            document.body.style.backgroundColor = '#003300';
        }
    }, 1500);
}

Live Preview

Preview not applicable.
This prank primarily affects the developer console. Copy the code and run it yourself for the full effect!

12. Starfield Background

A beautiful animated background of stars moving towards the viewer. This uses a canvas to create a simple 3D effect by updating star positions and sizes in a render loop.

const canvas = document.getElementById('starfield');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const stars = 800;
const speed = 0.05;
let starArray = [];
for (let i = 0; i < stars; i++) {
  starArray.push({
    x: Math.random() * canvas.width * 2 - canvas.width,
    y: Math.random() * canvas.height * 2 - canvas.height,
    z: Math.random() * canvas.width,
  });
}
function animate() {
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.save();
    ctx.translate(canvas.width / 2, canvas.height / 2);
    starArray.forEach(star => {
        star.z -= speed;
        if (star.z <= 0) { star.z = canvas.width; }
        const scale = canvas.width / star.z;
        const px = star.x * scale;
        const py = star.y * scale;
        const radius = Math.max(scale, 0.1);
        ctx.beginPath();
        ctx.arc(px, py, radius, 0, 2 * Math.PI);
        ctx.fill();
    });
    ctx.restore();
    requestAnimationFrame(animate);
}
animate();

Live Preview

13. Get Query Parameter from URL

A simple but incredibly common need. This function retrieves the value of a specific query parameter from the current URL using the modern `URLSearchParams` API.

const getQueryParam = (param) => {
    const urlParams = new URLSearchParams(window.location.search);
    return urlParams.get(param);
};
// --- Example Usage ---
// If the URL is "yourpage.html?user=shane&id=123"
const user = getQueryParam('user'); // Returns "shane"
const id = getQueryParam('id');   // Returns "123"

Live Preview

14. Confetti Cannon

Celebrate success! This function creates a burst of colorful confetti particles that animate with gravity and fade out, perfect for successful form submissions or completing a task.

function launchConfetti() {
    // Requires the 'canvas-confetti' library
    // 
    const count = 200;
    const defaults = { origin: { y: 0.7 } };
    function fire(particleRatio, opts) {
        confetti({ ...defaults, ...opts, particleCount: Math.floor(count * particleRatio) });
    }
    fire(0.25, { spread: 26, startVelocity: 55 });
    fire(0.2, { spread: 60 });
    fire(0.35, { spread: 100, decay: 0.91, scalar: 0.8 });
    fire(0.1, { spread: 120, startVelocity: 25, decay: 0.92, scalar: 1.2 });
    fire(0.1, { spread: 120, startVelocity: 45 });
}

Live Preview

15. Draggable Element

A highly reusable utility to make any HTML element draggable. This demonstrates listening to mouse down, move, and up events to manage state.

function makeDraggable(element) {
    let isDragging = false, offsetX, offsetY;
    element.addEventListener('mousedown', (e) => {
        isDragging = true;
        offsetX = e.clientX - element.offsetLeft;
        offsetY = e.clientY - element.offsetTop;
        element.style.cursor = 'grabbing';
    });
    document.addEventListener('mousemove', (e) => {
        if (!isDragging) return;
        element.style.left = `${e.clientX - offsetX}px`;
        element.style.top = `${e.clientY - offsetY}px`;
    });
    document.addEventListener('mouseup', () => {
        isDragging = false;
        element.style.cursor = 'grab';
    });
}
// --- CSS & HTML Required ---
// #draggable { position: absolute; cursor: grab; ... }
makeDraggable(document.getElementById('draggable'));

Live Preview

16. Throttle Function

Similar to debounce, but throttle guarantees execution of the function regularly, at most once every X milliseconds. Perfect for handling scroll or mouse-move events without overwhelming the browser.

const throttle = (func, limit) => {
    let inThrottle;
    return function(...args) {
        const context = this;
        if (!inThrottle) {
            func.apply(context, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
};
// --- Example Usage ---
const throttledFunc = throttle(() => {
    console.log('Function throttled at 1000ms!');
}, 1000);
window.addEventListener('scroll', throttledFunc);

Live Preview

Preview not applicable.
This utility's effect is best seen by observing console logs while performing an action like scrolling.

17. Async Sleep Function

A simple yet powerful helper that pauses execution in an `async` function for a specified duration. Cleaner and more readable than using `setTimeout` for delays in sequential asynchronous tasks.

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// --- Example Usage ---
async function showMessages() {
    const output = document.getElementById('status');
    output.textContent = 'Starting...';
    await sleep(2000);
    output.textContent = 'Processing... (Slept for 2s)';
    await sleep(3000);
    output.textContent = 'Complete! (Slept for 3s)';
}
showMessages();

Live Preview

18. Shuffle an Array

A classic and essential algorithm. This function shuffles an array in-place using the Fisher-Yates (aka Knuth) shuffle algorithm, which provides an unbiased permutation.

function shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]]; // ES6 swap
    }
    return array;
}
// --- Example Usage ---
const emojis = ['', '', '', '浪', '', ''];
shuffleArray(emojis);
console.log(emojis);

Live Preview

19. Is Element In Viewport?

A crucial function for performance optimizations like lazy loading images or triggering animations only when an element scrolls into view. It uses the modern `IntersectionObserver` for efficiency.

function onElementInView(element, callback, options = { threshold: 0.1 }) {
    const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                callback(entry.target);
                // Optional: stop observing once it has been seen
                observer.unobserve(entry.target);
            }
        });
    }, options);
    observer.observe(element);
}
// --- Example Usage ---
const myElement = document.getElementById('lazy-load-div');
onElementInView(myElement, (el) => {
    el.textContent = 'I am in view!';
    el.style.backgroundColor = '#8b5cf6';
});

Live Preview

This function is best demonstrated on a long, scrollable page. The logic is used on the Shane Studios homepage to animate cards into view.

20. 3D Tilting Card Effect

A very popular and stylish UI effect. This snippet uses CSS custom properties and JavaScript to make a card tilt in 3D space based on the mouse position over it.

// HTML: 
...
// CSS: .tilt-card { transform: perspective(1000px) ... } const card = document.querySelector('.tilt-card'); card.addEventListener('mousemove', (e) => { const { top, left, width, height } = card.getBoundingClientRect(); const x = e.clientX - left; const y = e.clientY - top; const midX = width / 2; const midY = height / 2; const rotateX = (y - midY) / 7; const rotateY = (x - midX) / -7; card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.05)`; }); card.addEventListener('mouseleave', () => { card.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)'; });

Live Preview

21. Water Ripple Click Effect

An impressive visual effect that creates a "water ripple" originating from the user's click location on an element, using a dynamically created and animated element.

function applyRippleEffect(element) {
    element.addEventListener('click', function(e) {
        const rect = element.getBoundingClientRect();
        const ripple = document.createElement('span');
        const diameter = Math.max(element.clientWidth, element.clientHeight);
        const radius = diameter / 2;
        ripple.style.width = ripple.style.height = `${diameter}px`;
        ripple.style.left = `${e.clientX - rect.left - radius}px`;
        ripple.style.top = `${e.clientY - rect.top - radius}px`;
        ripple.classList.add('ripple');
        const existingRipple = element.getElementsByClassName('ripple')[0];
        if (existingRipple) {
            existingRipple.remove();
        }
        element.appendChild(ripple);
    });
}
// --- CSS Required ---
// .ripple-container { position: relative; overflow: hidden; }
// .ripple { position: absolute; border-radius: 50%; background: rgba(255,255,255,0.7); transform: scale(0); animation: ripple .6s linear; }
// @keyframes ripple { to { transform: scale(4); opacity: 0; } }

Live Preview

22. Detect Mobile Device

A simple but effective check to determine if the user is on a mobile device. It tests for touch events and checks the user agent string for common mobile keywords.

function isMobile() {
    let hasTouch = false;
    try {
        document.createEvent('TouchEvent');
        hasTouch = true;
    } catch (e) {
        hasTouch = false;
    }
    const userAgentCheck = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
    return hasTouch || userAgentCheck.test(navigator.userAgent);
}
// --- Example Usage ---
const statusDiv = document.getElementById('device-status');
if (isMobile()) {
    statusDiv.textContent = 'Device: Mobile';
} else {
    statusDiv.textContent = 'Device: Desktop';
}

Live Preview

23. Cookie Management Functions

The classic trio of functions for managing browser cookies: `setCookie`, `getCookie`, and `deleteCookie`. Essential for tasks that require simple, client-side persistence.

function setCookie(name, value, days) {
    let expires = "";
    if (days) {
        const date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        expires = "; expires=" + date.toUTCString();
    }
    document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
function getCookie(name) {
    const nameEQ = name + "=";
    const ca = document.cookie.split(';');
    for(let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
}
function deleteCookie(name) {   
    document.cookie = name +'=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
}

Live Preview

Cookie operations affect the entire document and are best tested on a live page rather than a sandboxed iframe.

24. Format Number as Currency

A very practical function that uses the powerful `Intl.NumberFormat` API to correctly format a number as currency for a specific locale, including the correct currency symbol.

function formatAsCurrency(number, locale = 'en-US', currency = 'USD') {
    return new Intl.NumberFormat(locale, {
        style: 'currency',
        currency: currency,
    }).format(number);
}
// --- Example Usage ---
const price1 = formatAsCurrency(12345.67); // $12,345.67
const price2 = formatAsCurrency(12345.67, 'de-DE', 'EUR'); // 12.345,67 €
const price3 = formatAsCurrency(12345.67, 'ja-JP', 'JPY'); // ¥12,346
console.log(price1, price2, price3);

Live Preview

25. Mouse-Tracking Gradient Glow

A sleek, modern effect that sets a radial gradient on an element's background that follows the user's mouse, creating a "spotlight" or "glow" effect.

// HTML: 
...
const cards = document.querySelectorAll('.glow-card'); cards.forEach(card => { card.addEventListener('mousemove', e => { const rect = card.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; card.style.setProperty('--mouse-x', `${x}px`); card.style.setProperty('--mouse-y', `${y}px`); }); }); // --- CSS Required --- // .glow-card::before { content: ''; ... background: radial-gradient(circle at var(--mouse-x) var(--mouse-y), ...); }

Live Preview

26. Copy Text to Clipboard Function

A self-contained async function that copies any given text to the clipboard using the modern, secure `navigator.clipboard` API. It returns a promise that resolves on success and rejects on failure.

async function copyToClipboard(text) {
    if (!navigator.clipboard) {
        // Fallback for older browsers
        const textArea = document.createElement("textarea");
        textArea.value = text;
        textArea.style.position = "fixed"; // Avoid scrolling to bottom
        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();
        try {
            document.execCommand('copy');
            document.body.removeChild(textArea);
            return Promise.resolve();
        } catch (err) {
            document.body.removeChild(textArea);
            return Promise.reject(err);
        }
    }
    return navigator.clipboard.writeText(text);
}
// --- Example Usage ---
copyToClipboard('Hello from Shane Studios!')
    .then(() => console.log('Copied successfully!'))
    .catch(() => console.error('Failed to copy.'));

Live Preview

Preview not applicable.
This is a utility function used by the "Copy" buttons on this very page!

27. Get Random Array Element

A fundamental and frequently needed utility. This one-liner function returns a random element from any given array.

const getRandomElement = (arr) => arr[Math.floor(Math.random() * arr.length)];
// --- Example Usage ---
const choices = ['Rock', 'Paper', 'Scissors'];
const randomChoice = getRandomElement(choices);
console.log(randomChoice); // Logs one of the three choices

const colors = ['#5e81f2', '#8b5cf6', '#f87171', '#4ade80'];
document.body.style.backgroundColor = getRandomElement(colors);

Live Preview

28. Online/Offline Status Detector

Useful for modern web apps (especially PWAs) that need to behave differently when the user loses their internet connection. This snippet uses built-in browser events to detect connectivity changes.

const statusEl = document.getElementById('connection-status');
const updateOnlineStatus = () => {
    const isOnline = navigator.onLine;
    statusEl.textContent = isOnline ? 'Status: Online' : 'Status: Offline';
    statusEl.style.color = isOnline ? '#4ade80' : '#f87171';
};
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
// Initial check
updateOnlineStatus();

Live Preview

29. Text Reveal on Scroll

A stylish effect where lines of text are revealed one by one as the user scrolls down the page. This uses `IntersectionObserver` to trigger the animation on a parent container.

// HTML: 

Line 1

Line 2

...
// CSS: .text-reveal-container p { transform: translateY(100%); opacity: 0; transition: ... } // .text-reveal-container.in-view p { transform: translateY(0); opacity: 1; } const container = document.querySelector('.text-reveal-container'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('in-view'); } }); }, { threshold: 0.5 }); observer.observe(container);

Live Preview

30. Page Fade-Out Prank

A simple but effective prank to play on colleagues. This function slowly fades the entire page to black over a long period, making the user think their screen is failing.

function gentleFadeToBlack(durationInSeconds = 60) {
    const overlay = document.createElement('div');
    overlay.style.position = 'fixed';
    overlay.style.top = '0';
    overlay.style.left = '0';
    overlay.style.width = '100vw';
    overlay.style.height = '100vh';
    overlay.style.backgroundColor = 'black';
    overlay.style.opacity = '0';
    overlay.style.zIndex = '999999';
    overlay.style.pointerEvents = 'none'; // So it doesn't block clicks initially
    overlay.style.transition = `opacity ${durationInSeconds}s linear`;
    document.body.appendChild(overlay);
    // The timeout ensures the transition property is applied before we change opacity
    setTimeout(() => {
        overlay.style.opacity = '1';
    }, 50);
}
// --- Example Usage ---
// Call this from the console to prank someone!
// gentleFadeToBlack(300); // Fades out over 5 minutes

Live Preview

This prank is best executed from the console on an unsuspecting friend's machine. A live preview would spoil the fun!