BROKEN SOLID - RETROSCOPIC CRT SHADER SOURCE CODE
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
body { margin: 0; overflow: hidden; background: #000; }
#stage {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
filter: grayscale(1) sepia(1) hue-rotate(80deg) saturate(5) brightness(1.15) contrast(1.2);
z-index: 2;
}
/* Retroscopic Overlay */
#scope {
position: fixed;
inset: 0;
z-index: 4;
pointer-events: none;
display: block;
}
#scope>div {
position: absolute;
inset: 0;
}
#scope .glow {
background: radial-gradient(circle at 50% 46%, rgba(30, 255, 130, .08), rgba(0, 0, 0, 0) 62%);
animation: nvflick 3.6s steps(24) infinite;
}
#scope .scan {
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0) 0 2px, rgba(0, 25, 8, .45) 2px 3px);
animation: nvscan 7s linear infinite;
}
#scope .vig {
box-shadow: inset 0 0 200px 50px rgba(0, 0, 0, .8);
}
#scope .mask {
background: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0) 54%, rgba(0, 0, 0, .5) 72%, rgba(0, 0, 0, .94) 100%);
}
@keyframes nvscan {
from { background-position: 0 0; }
to { background-position: 0 240px; }
}
@keyframes nvflick {
0%, 100% { opacity: .55; }
40% { opacity: .9; }
62% { opacity: .4; }
80% { opacity: .75; }
}
</style>
</head>
<body>
<div id="scope">
<div class="glow"></div>
<div class="scan"></div>
<div class="vig"></div>
<div class="mask"></div>
</div>
<div id="stage"></div>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('stage').appendChild(renderer.domElement);
scene.add(new THREE.AmbientLight(0xffffff, 0.6));
const light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.set(5, 5, 5);
scene.add(light);
const a = 3;
const vertices = [
new THREE.Vector3(a, a, 3*a),
new THREE.Vector3(a, 3*a, a),
new THREE.Vector3(3*a, a, a),
new THREE.Vector3(-a, -a, 3*a),
new THREE.Vector3(-a, 3*a, -a),
new THREE.Vector3(3*a, -a, -a),
new THREE.Vector3(a, -a, -3*a),
new THREE.Vector3(a, -3*a, -a),
new THREE.Vector3(-3*a, -a, -a),
new THREE.Vector3(-a, a, -3*a),
new THREE.Vector3(-a, -3*a, a),
new THREE.Vector3(-3*a, a, a)
];
vertices.forEach(v => v.multiplyScalar(0.2));
const indices = [
// Triangle faces
0, 1, 2,
3, 4, 5,
6, 7, 8,
9, 10, 11,
0, 3, 5, 0, 5, 2, 0, 2, 4, 0, 4, 3,
1, 0, 4, 1, 4, 7, 1, 7, 10, 1, 10, 0,
2, 1, 7, 2, 7, 6, 2, 6, 5, 2, 5, 1,
3, 8, 11, 3, 11, 9, 3, 9, 4, 3, 4, 8
];
const geometry = new THREE.BufferGeometry();
const positions = [];
vertices.forEach(v => positions.push(v.x, v.y, v.z));
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setIndex(indices);
geometry.computeVertexNormals();
const faceMaterial = new THREE.MeshPhongMaterial({
color: 0x00bcd4,
transparent: true,
opacity: 0.9
});
const wireMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: true,
transparent: true,
opacity: 0.3
});
const shape = new THREE.Group();
shape.add(new THREE.Mesh(geometry, faceMaterial));
shape.add(new THREE.Mesh(geometry, wireMaterial));
scene.add(shape);
camera.position.z = 4;
function animate() {
requestAnimationFrame(animate);
shape.rotation.x += 0.005;
shape.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>