unbenannt

Pasted Oct 21, 2026, 9:36 PM1 file295 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgsq2w7s738vex7
unbenannt
Here is an even more interactive version built directly into an embedded, playable simulation right here in the chat!
You can use your mouse to:
 * **Click and Drag** to orbit around the black hole.
 * **Scroll** to zoom in and out.
 * Use the control panel on the top right to adjust the accretion disk speed, brightness, gravitational lens glow, and plasma jets in real time.
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Embedded 3D Black Hole Sandbox</title>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: #000;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        #canvas-container {
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
        }
        #info {
            position: absolute;
            top: 20px;
            left: 20px;
            color: #ffffff;
            z-index: 100;
            pointer-events: none;
            text-shadow: 0 0 5px rgba(0,0,0,0.8);
        }
        h1 { margin: 0 0 5px 0; font-size: 18px; }
        p { margin: 0; font-size: 11px; opacity: 0.7; }
    </style>
</head>
<body>

    <div id="info">
        <h1>Black Hole Playground</h1>
        <p>Drag to rotate | Scroll to zoom | Adjust sliders on right</p>
    </div>

    <div id="canvas-container"></div>

    <!-- Three.js, OrbitControls, and lil-gui CDN -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lil-gui@0.18"></script>

    <script>
        const container = document.getElementById('canvas-container');

        // --- Configuration & Parameters Object ---
        const params = {
            diskSpeed: 0.2,
            diskOpacity: 0.75,
            haloGlow: 1.0,
            jetIntensity: 0.6,
            eventHorizonSize: 4.0,
            autoRotate: false
        };

        // --- 1. Scene & Setup ---
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 0.1, 1000);
        camera.position.set(0, 15, 35);

        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(container.clientWidth, container.clientHeight);
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        container.appendChild(renderer.domElement);

        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.maxDistance = 100;
        controls.minDistance = 5;

        // --- 2. Starfield Background ---
        const starGeometry = new THREE.BufferGeometry();
        const starCount = 4000;
        const starPositions = new Float32Array(starCount * 3);

        for (let i = 0; i < starCount * 3; i += 3) {
            starPositions[i] = (Math.random() - 0.5) * 400;
            starPositions[i + 1] = (Math.random() - 0.5) * 400;
            starPositions[i + 2] = (Math.random() - 0.5) * 400;
        }

        starGeometry.setAttribute('position', new THREE.BufferAttribute(starPositions, 3));
        const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.6, transparent: true, opacity: 0.8 });
        const starField = new THREE.Points(starGeometry, starMaterial);
        scene.add(starField);

        // --- 3. Event Horizon (Black Sphere) ---
        const eventHorizonGeo = new THREE.SphereGeometry(params.eventHorizonSize, 64, 64);
        const eventHorizonMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
        const eventHorizon = new THREE.Mesh(eventHorizonGeo, eventHorizonMat);
        scene.add(eventHorizon);

        // --- 4. Gravitational Lensing Halo ---
        const haloGeo = new THREE.SphereGeometry(params.eventHorizonSize * 1.15, 64, 64);
        const haloMat = new THREE.ShaderMaterial({
            vertexShader: `
                varying vec3 vNormal;
                void main() {
                    vNormal = normalize(normalMatrix * normal);
                    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                }
            `,
            fragmentShader: `
                varying vec3 vNormal;
                uniform float uGlow;
                void main() {
                    float intensity = pow(0.6 - dot(vNormal, vec3(0, 0, 1.0)), 3.0) * uGlow;
                    gl_FragColor = vec4(1.0, 0.4, 0.1, 1.0) * intensity;
                }
            `,
            uniforms: {
                uGlow: { value: params.haloGlow }
            },
            blending: THREE.AdditiveBlending,
            side: THREE.BackSide,
            transparent: true
        });
        const halo = new THREE.Mesh(haloGeo, haloMat);
        scene.add(halo);

        // --- 5. Accretion Disk ---
        const particleCount = 14000;
        const diskGeometry = new THREE.BufferGeometry();
        const positions = new Float32Array(particleCount * 3);
        const colors = new Float32Array(particleCount * 3);

        const innerRadius = params.eventHorizonSize * 1.3;
        const outerRadius = 22;

        const colorInner = new THREE.Color(0xfff0bb); 
        const colorMid = new THREE.Color(0xff6600);   
        const colorOuter = new THREE.Color(0xaa0000); 

        for (let i = 0; i < particleCount; i++) {
            const radius = innerRadius + Math.pow(Math.random(), 1.5) * (outerRadius - innerRadius);
            const angle = Math.random() * Math.PI * 2;
            const height = (Math.random() - 0.5) * (radius * 0.08);

            positions[i * 3] = Math.cos(angle) * radius;
            positions[i * 3 + 1] = height;
            positions[i * 3 + 2] = Math.sin(angle) * radius;

            const t = (radius - innerRadius) / (outerRadius - innerRadius);
            let particleColor = new THREE.Color();
            if (t < 0.3) {
                particleColor.lerpColors(colorInner, colorMid, t / 0.3);
            } else {
                particleColor.lerpColors(colorMid, colorOuter, (t - 0.3) / 0.7);
            }

            colors[i * 3] = particleColor.r;
            colors[i * 3 + 1] = particleColor.g;
            colors[i * 3 + 2] = particleColor.b;
        }

        diskGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        diskGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

        const diskMaterial = new THREE.PointsMaterial({
            size: 0.35,
            vertexColors: true,
            blending: THREE.AdditiveBlending,
            transparent: true,
            opacity: params.diskOpacity
        });

        const accretionDisk = new THREE.Points(diskGeometry, diskMaterial);
        scene.add(accretionDisk);

        // --- 6. Relativistic Jets ---
        const jetCount = 800;
        function createJetData() {
            const pos = new Float32Array(jetCount * 3);
            const col = new Float32Array(jetCount * 3);
            for (let i = 0; i < jetCount; i++) {
                col[i * 3] = 0.4;
                col[i * 3 + 1] = 0.7;
                col[i * 3 + 2] = 1.0;
            }
            return { pos, col };
        }

        const topJetData = createJetData();
        const bottomJetData = createJetData();

        const topJetGeo = new THREE.BufferGeometry();
        topJetGeo.setAttribute('position', new THREE.BufferAttribute(topJetData.pos, 3));
        topJetGeo.setAttribute('color', new THREE.BufferAttribute(topJetData.col, 3));

        const bottomJetGeo = new THREE.BufferGeometry();
        bottomJetGeo.setAttribute('position', new THREE.BufferAttribute(bottomJetData.pos, 3));
        bottomJetGeo.setAttribute('color', new THREE.BufferAttribute(bottomJetData.col, 3));

        const jetMat = new THREE.PointsMaterial({
            size: 0.25,
            vertexColors: true,
            blending: THREE.AdditiveBlending,
            transparent: true,
            opacity: params.jetIntensity
        });

        const topJet = new THREE.Points(topJetGeo, jetMat);
        const bottomJet = new THREE.Points(bottomJetGeo, jetMat);
        scene.add(topJet);
        scene.add(bottomJet);

        function updateJets() {
            const tPos = topJetGeo.attributes.position.array;
            const bPos = bottomJetGeo.attributes.position.array;
            for (let i = 0; i < jetCount; i++) {
                let h1 = tPos[i * 3 + 1] + 0.4;
                if (h1 > 30) h1 = params.eventHorizonSize;
                let spread1 = h1 * 0.08;
                let angle1 = i + performance.now() * 0.002;
                tPos[i * 3] = Math.cos(angle1) * spread1;
                tPos[i * 3 + 1] = h1;
                tPos[i * 3 + 2] = Math.sin(angle1) * spread1;

                let h2 = bPos[i * 3 + 1] - 0.4;
                if (h2 < -30) h2 = -params.eventHorizonSize;
                let spread2 = Math.abs(h2) * 0.08;
                let angle2 = i - performance.now() * 0.002;
                bPos[i * 3] = Math.cos(angle2) * spread2;
                bPos[i * 3 + 1] = h2;
                bPos[i * 3 + 2] = Math.sin(angle2) * spread2;
            }
            topJetGeo.attributes.position.needsUpdate = true;
            bottomJetGeo.attributes.position.needsUpdate = true;
        }

        // --- 7. Setup GUI Controls ---
        const gui = new GUI();
        gui.domElement.style.position = 'absolute';
        gui.domElement.style.top = '10px';
        gui.domElement.style.right = '10px';
        container.appendChild(gui.domElement);

        gui.add(params, 'diskSpeed', 0, 1.0, 0.01).name('Disk Rotation');
        gui.add(params, 'diskOpacity', 0.1, 1.0, 0.05).name('Disk Brightness').onChange(v => diskMaterial.opacity = v);
        gui.add(params, 'haloGlow', 0.0, 3.0, 0.1).name('Lensing Glow').onChange(v => haloMat.uniforms.uGlow.value = v);
        gui.add(params, 'jetIntensity', 0.0, 1.0, 0.05).name('Jet Intensity').onChange(v => jetMat.opacity = v);
        gui.add(params, 'autoRotate').name('Auto Orbit Cam');

        // --- 8. Animation Loop ---
        const clock = new THREE.Clock();

        function animate() {
            requestAnimationFrame(animate);
            const time = clock.getElapsedTime();

            accretionDisk.rotation.y = time * params.diskSpeed;

            updateJets();
            topJet.rotation.y = -time * 0.5;
            bottomJet.rotation.y = -time * 0.5;

            if (params.autoRotate) {
                camera.position.x = Math.sin(time * 0.2) * 35;
                camera.position.z = Math.cos(time * 0.2) * 35;
                camera.lookAt(0, 0, 0);
            }

            controls.update();
            renderer.render(scene, camera);
        }

        animate();

        // --- 9. Window Resize Handling ---
        window.addEventListener('resize', () => {
            camera.aspect = container.clientWidth / container.clientHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(container.clientWidth, container.clientHeight);
        });
    </script>
</body>
</html>

```

11,502 chars · codepastes.com