Platform Requirement: This script works on PC only (Tested in Windows, macOS and  Linux I have no idea but it should work). 


How to Install

  1. Install a Userscript Manager: Open Google Chrome and install the Tampermonkey extension from the Chrome Web Store.

  2. Create a New Script:

    • Click the Tampermonkey icon in your browser toolbar.

    • Select Create a new script.

  3. Paste the Code:

    • Delete any default text in the editor.

    • Copy the entire script code provided bellow.

    • Paste it into the Tampermonkey editor.

  4. Save: Press Ctrl + S or go to File > Save within the Tampermonkey editor.


How to Use

  1. Launch the Game: Navigate to https://angeldu.st/static/webgl-new/.

  2. Open the Menu: You will see a small button labeled 3D in the top-left corner of the screen.

  3. Enable Effect: Click the 3D button to open the settings panel. Check the Enable 3D Effect box.

  4. Hardware: You must wear standard Red-Cyan 3D glasses to see the effect.

  5. Adjust Settings: Use the sliders to change "Eye Separation" and "Depth Curve" to match your screen size and comfort level.


// ==UserScript==
// @name         Angeldust 3D Anaglyph
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Adds stereoscopic red-cyan 3D effect
// @author       Ripie and a select number of artificial sweeteners
// @match        https://angeldu.st/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    console.log("Angeldust 3D Anaglyph (Depth-Based): Initializing...");

    let glContext = null;
    let activeProgram = null;
    let depthTexture = null;
    let currentFramebuffer = null;

    let settings = {
        enabled: true,
        separation: 10.0,  // Pixel offset
        depthPower: -0.2,  // Depth curve
        debugDepth: false,
        menuVisible: false
    };

    // --- UI SETUP ---
    function createUI() {
        if (document.getElementById('ad-3d-ui')) return;

        const toggleBtn = document.createElement('div');
        Object.assign(toggleBtn.style, {
            position: 'fixed', top: '10px', left: '10px', zIndex: '100000',
            width: '30px', height: '30px', background: '#333', color: '#f00',
            border: '1px solid #0ff', borderRadius: '4px', cursor: 'pointer',
            display: 'flex', justifyContent: 'center', alignItems: 'center',
            fontSize: '20px', fontWeight: 'bold'
        });
        toggleBtn.innerText = '3D';
        toggleBtn.onclick = () => {
            settings.menuVisible = !settings.menuVisible;
            mainPanel.style.display = settings.menuVisible ? 'block' : 'none';
        };
        document.body.appendChild(toggleBtn);

        const mainPanel = document.createElement('div');
        mainPanel.id = 'ad-3d-ui';
        Object.assign(mainPanel.style, {
            position: 'fixed', top: '50px', left: '10px', zIndex: '99999',
            backgroundColor: 'rgba(10, 10, 10, 0.95)', padding: '10px',
            color: '#0ff', border: '2px solid #0ff', fontFamily: 'monospace',
            width: '260px', borderRadius: '8px', display: 'none'
        });

        const title = document.createElement('div');
        title.innerText = "3D ANAGLYPH (DEPTH)";
        title.style.textAlign = "center";
        title.style.fontWeight = "bold";
        title.style.marginBottom = "10px";
        title.style.color = "#f00";
        title.style.textShadow = "2px 2px #0ff";
        mainPanel.appendChild(title);

        // Enable Toggle
        const toggleRow = document.createElement('div');
        toggleRow.style.marginBottom = '10px';
        const toggleCheck = document.createElement('input');
        toggleCheck.type = 'checkbox';
        toggleCheck.checked = settings.enabled;
        toggleCheck.onchange = () => { settings.enabled = toggleCheck.checked; };
        const toggleLabel = document.createElement('label');
        toggleLabel.innerText = ' Enable 3D Effect';
        toggleLabel.style.fontSize = '12px';
        toggleRow.appendChild(toggleCheck);
        toggleRow.appendChild(toggleLabel);
        mainPanel.appendChild(toggleRow);

        // Debug Toggle
        const debugRow = document.createElement('div');
        debugRow.style.marginBottom = '10px';
        const debugCheck = document.createElement('input');
        debugCheck.type = 'checkbox';
        debugCheck.checked = settings.debugDepth;
        debugCheck.onchange = () => { settings.debugDepth = debugCheck.checked; };
        const debugLabel = document.createElement('label');
        debugLabel.innerText = ' Debug Depth View';
        debugLabel.style.fontSize = '12px';
        debugLabel.style.color = '#ff0';
        debugRow.appendChild(debugCheck);
        debugRow.appendChild(debugLabel);
        mainPanel.appendChild(debugRow);

        // Sliders
        const addSlider = (label, key, min, max, scale) => {
            const row = document.createElement('div');
            row.style.marginBottom = '8px';
            const txt = document.createElement('div');
            txt.style.fontSize = '11px';
            const range = document.createElement('input');
            range.type = 'range';
            range.min = min;
            range.max = max;
            range.style.width = '100%';

            const update = () => {
                settings[key] = parseInt(range.value) / scale;
                txt.innerText = `${label}: ${(settings[key]).toFixed(1)}`;
            };
            range.value = settings[key] * scale;
            range.oninput = update;
            update();
            row.appendChild(txt);
            row.appendChild(range);
            mainPanel.appendChild(row);
        };

        addSlider("Eye Separation", "separation", -100, 100, 10.0);
        addSlider("Depth Curve", "depthPower", -10.0, 50, 10.0);

        const info = document.createElement('div');
        info.style.marginTop = '10px';
        info.style.fontSize = '10px';
        info.style.borderTop = '1px solid #333';
        info.style.paddingTop = '8px';
        info.innerHTML = 'DEPTH-BASED MODE
Uses GL depth buffer.

Use red-cyan glasses
(Red=Left, Cyan=Right)'; mainPanel.appendChild(info); document.body.appendChild(mainPanel); } const loader = () => { if (!document.body) requestAnimationFrame(loader); else createUI(); }; if (document.readyState === 'complete') loader(); else window.addEventListener('load', loader); // --- WEBGL HOOKS --- const gl = WebGLRenderingContext.prototype; const _shaderSource = gl.shaderSource; const _useProgram = gl.useProgram; const _drawElements = gl.drawElements; const _getUniform = gl.getUniformLocation; const _uniform1f = gl.uniform1f; const _uniform1i = gl.uniform1i; const _bindFramebuffer = gl.bindFramebuffer; const _activeTexture = gl.activeTexture; const _bindTexture = gl.bindTexture; // Track framebuffer to know when we're rendering to screen gl.bindFramebuffer = function(target, framebuffer) { currentFramebuffer = framebuffer; return _bindFramebuffer.call(this, target, framebuffer); }; // --- SHADER INJECTION --- gl.shaderSource = function(shader, source) { let modified = source; // TARGET if (source.includes("AD_SHADER_IS_SCREEN_COMPOSITING") && source.includes("void main()")) { if (!modified.includes("uniform float u_3D_Enabled;")) { const header = "precision mediump float;"; modified = modified.replace(header, header + "\n" + "uniform float u_3D_Enabled;\n" + "uniform float u_3D_Separation;\n" + "uniform float u_3D_DepthPower;\n" + "uniform float u_3D_DebugDepth;\n" + "uniform sampler2D u_3D_DepthTexture;\n"); } const finalOutputRegex = /(gl_FragColor\s*=\s*vec4\(linearToSRGB\(renderColor \+ postProcessed\),\s*1\.0\);)/; if (finalOutputRegex.test(modified)) { modified = modified.replace(finalOutputRegex, ` // --- 3D ANAGLYPH POST-PROCESS --- vec3 finalColor = renderColor + postProcessed; if (u_3D_DebugDepth > 0.5) { // Debug: Show OpenGL depth buffer float rawDepth = texture2D(u_3D_DepthTexture, uv).r; // Linearize depth for visualization float near = 0.1; float far = 1000.0; float linearDepth = (2.0 * near) / (far + near - rawDepth * (far - near)); finalColor = vec3(linearDepth * 10.0); // Scale for visibility } else if (u_3D_Enabled > 0.5) { float rawDepth = texture2D(u_3D_DepthTexture, uv).r; float near = 0.1; float far = 1000.0; float linearDepth = (2.0 * near) / (far + near - rawDepth * (far - near)); float depth = pow(clamp(linearDepth, 0.0, 1.0), u_3D_DepthPower); float pixelOffset = (u_3D_Separation / uniform_TextureSize.x) * (1.0 - depth); vec2 leftUV = uv + vec2(pixelOffset, 0.0); vec3 leftColor = hdrToSDR(texture2D(uniform_TextureMaterial, leftUV)); vec3 leftPost = dualFilterUpsample(uniform_TexturePostProcessed, leftUV, halfpixel); vec2 rightUV = uv - vec2(pixelOffset, 0.0); vec3 rightColor = hdrToSDR(texture2D(uniform_TextureMaterial, rightUV)); vec3 rightPost = dualFilterUpsample(uniform_TexturePostProcessed, rightUV, halfpixel); finalColor = vec3( (leftColor.r + leftPost.r) * 1.1, (rightColor.g + rightPost.g), (rightColor.b + rightPost.b) ); } gl_FragColor = vec4(linearToSRGB(finalColor), 1.0); // --- END 3D ANAGLYPH --- `); } } return _shaderSource.call(this, shader, modified); }; // Update uniforms and bind depth texture gl.useProgram = function(program) { const result = _useProgram.call(this, program); activeProgram = program; if (program && !program.has3DLocs) { program.u_3D_Enabled = _getUniform.call(this, program, "u_3D_Enabled"); program.u_3D_Separation = _getUniform.call(this, program, "u_3D_Separation"); program.u_3D_DepthPower = _getUniform.call(this, program, "u_3D_DepthPower"); program.u_3D_DebugDepth = _getUniform.call(this, program, "u_3D_DebugDepth"); program.u_3D_DepthTexture = _getUniform.call(this, program, "u_3D_DepthTexture"); if (program.u_3D_Enabled) { console.log("3D uniforms bound to post-processing shader"); } program.has3DLocs = true; glContext = this; } return result; }; gl.drawElements = function(mode, count, type, offset) { if (activeProgram && activeProgram.u_3D_Enabled) { // Only apply to final screen render (framebuffer = null) if (currentFramebuffer === null) { _uniform1f.call(this, activeProgram.u_3D_DebugDepth, settings.debugDepth ? 1.0 : 0.0); _uniform1f.call(this, activeProgram.u_3D_Enabled, settings.enabled ? 1.0 : 0.0); if (settings.enabled || settings.debugDepth) { _uniform1f.call(this, activeProgram.u_3D_Separation, settings.separation); _uniform1f.call(this, activeProgram.u_3D_DepthPower, settings.depthPower); // Bind depth texture to texture unit 7 if (activeProgram.u_3D_DepthTexture) { _activeTexture.call(this, this.TEXTURE7); _bindTexture.call(this, this.TEXTURE_2D, depthTexture); _uniform1i.call(this, activeProgram.u_3D_DepthTexture, 7); _activeTexture.call(this, this.TEXTURE0); } } } } return _drawElements.call(this, mode, count, type, offset); }; // Capture the depth texture when it's created const _texImage2D = gl.texImage2D; gl.texImage2D = function(target, level, internalformat, width, height, border, format, type, pixels) { const result = _texImage2D.apply(this, arguments); // Try to identify the depth texture // Depth textures are usually DEPTH_COMPONENT format if (format === this.DEPTH_COMPONENT || format === this.DEPTH_STENCIL) { const boundTex = this.getParameter(this.TEXTURE_BINDING_2D); if (boundTex) { depthTexture = boundTex; console.log("Captured depth texture:", depthTexture); } } return result; }; })();