Skip to content

Instantly share code, notes, and snippets.

@bosley
Last active December 11, 2024 07:02
Show Gist options
  • Save bosley/d06e72e6af5e0f4567ebc90bde6707ba to your computer and use it in GitHub Desktop.
Save bosley/d06e72e6af5e0f4567ebc90bde6707ba to your computer and use it in GitHub Desktop.
SSG 3D Vaporware adventure by Claude
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Digital Temple Paradise</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
}
canvas {
position: fixed;
}
.divine-messages {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #0f0;
font-family: monospace;
font-size: 24px;
mix-blend-mode: difference;
pointer-events: none;
z-index: 100;
text-shadow: 0 0 10px #0f0;
animation: glitch 0.3s infinite;
}
.holy-symbols {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 50;
mix-blend-mode: exclusion;
}
@keyframes glitch {
0% { transform: translate(-50%, -50%) skew(0deg); }
20% { transform: translate(-52%, -50%) skew(4deg); }
40% { transform: translate(-48%, -50%) skew(-4deg); }
60% { transform: translate(-50%, -52%) skew(2deg); }
80% { transform: translate(-50%, -48%) skew(-2deg); }
100% { transform: translate(-50%, -50%) skew(0deg); }
}
</style>
</head>
<body>
<div class="divine-messages"></div>
<svg class="holy-symbols" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="glitch">
<feTurbulence type="fractalNoise" baseFrequency="0.05" numOctaves="3" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="5"/>
</filter>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#0f0" stroke-width="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" filter="url(#glitch)"/>
</svg>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
class VaporwaveParadise {
#scene;
#camera;
#renderer;
#clock;
#terrain = [];
#gridSize = 2000;
#moveSpeed = 2;
#keys = {};
#mousePosition = { x: 0, y: 0 };
#euler = new THREE.Euler(0, 0, 0, 'YXZ');
constructor() {
this.#init();
this.#initDivineMessages();
}
#init() {
this.#clock = new THREE.Clock();
this.#scene = new THREE.Scene();
this.#scene.fog = new THREE.FogExp2(0x000000, 0.0008);
this.#camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 3000);
this.#camera.position.set(0, 30, 100);
this.#renderer = new THREE.WebGLRenderer({ antialias: true });
this.#renderer.setSize(window.innerWidth, window.innerHeight);
this.#renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(this.#renderer.domElement);
this.#createSunBackground();
this.#createMountains();
this.#createGrid();
this.#createPalmTrees();
this.#setupEventListeners();
this.#animate();
}
#createSunBackground() {
const sunGeometry = new THREE.PlaneGeometry(2000, 1000);
const sunMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
varying vec2 vUv;
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec3 getSunColor(vec2 uv) {
float dist = length(uv - vec2(0.5, 0.5));
vec3 sunColor = vec3(1.0, 0.2, 0.5);
vec3 skyColor = vec3(0.0, 0.0, 0.1);
// Digital corruption rings
float rings = sin(dist * 50.0 - time) * 0.5 + 0.5;
rings *= smoothstep(0.5, 0.2, dist);
// Digital noise
float noise = rand(uv * time);
if(noise > 0.97) rings = 1.0;
// Scanlines with glitch
float scanlines = sin(uv.y * 200.0 + time + noise * 10.0) * 0.5 + 0.5;
scanlines *= 0.2;
return mix(skyColor, sunColor, rings + scanlines) * (1.0 - dist);
}
void main() {
vec2 uv = vUv;
vec3 color = getSunColor(uv);
// Random digital corruption
if(rand(uv + time) > 0.99) {
color = vec3(1.0) - color;
}
gl_FragColor = vec4(color, 1.0);
}
`,
side: THREE.DoubleSide
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
sun.position.z = -1000;
sun.position.y = 300;
this.#scene.add(sun);
this.#terrain.push({ mesh: sun, material: sunMaterial });
}
#createMountains() {
const mountainGeometry = new THREE.PlaneGeometry(2000, 500, 100, 50);
const mountainMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: `
varying float vHeight;
varying vec3 vPosition;
uniform float time;
float noise(vec2 p) {
return sin(p.x * 0.1) * cos(p.y * 0.1) *
sin(p.x * 0.05 + time * 0.1) * 50.0;
}
void main() {
vPosition = position;
vec3 pos = position;
pos.y += noise(pos.xz);
vHeight = pos.y;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying float vHeight;
varying vec3 vPosition;
void main() {
vec3 baseColor = vec3(0.3, 0.0, 0.5);
vec3 highlightColor = vec3(1.0, 0.0, 0.5);
float heightFactor = smoothstep(-50.0, 50.0, vHeight);
vec3 finalColor = mix(baseColor, highlightColor, heightFactor);
// Add scanlines
float scanline = sin(vPosition.y * 0.1) * 0.1 + 0.9;
finalColor *= scanline;
gl_FragColor = vec4(finalColor, 1.0);
}
`,
side: THREE.DoubleSide
});
const mountains = new THREE.Mesh(mountainGeometry, mountainMaterial);
mountains.position.z = -500;
mountains.position.y = 0;
this.#scene.add(mountains);
this.#terrain.push({ mesh: mountains, material: mountainMaterial });
}
#createGrid() {
const gridMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: `
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
varying vec3 vPosition;
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec3 getDigitalColor(vec3 pos) {
float gridX = abs(mod(pos.x, 40.0) - 20.0);
float gridZ = abs(mod(pos.z, 40.0) - 20.0);
float line = min(gridX, gridZ);
line = smoothstep(0.5, 0.0, line);
// Temple OS inspired color palette
vec3 color1 = vec3(0.0, 1.0, 0.0); // Green
vec3 color2 = vec3(1.0, 0.0, 1.0); // Magenta
vec3 color3 = vec3(0.0, 1.0, 1.0); // Cyan
float t = time * 0.5;
float noise = rand(pos.xz * 0.01 + t);
vec3 lineColor = mix(color1, color2, sin(pos.z * 0.01 + time) * 0.5 + 0.5);
lineColor = mix(lineColor, color3, sin(pos.x * 0.01 + time * 1.3) * 0.5 + 0.5);
// Digital corruption effect
if(noise > 0.97) {
line *= 2.0;
lineColor = vec3(1.0);
}
return lineColor * line * (1.0 - length(pos.xz) * 0.001);
}
void main() {
vec3 color = getDigitalColor(vPosition);
float fadeOut = smoothstep(1000.0, 0.0, abs(vPosition.z));
gl_FragColor = vec4(color * fadeOut, 1.0);
}
`,
transparent: true,
blending: THREE.AdditiveBlending
});
const gridGeometry = new THREE.PlaneGeometry(2000, 2000, 200, 200);
const grid = new THREE.Mesh(gridGeometry, gridMaterial);
grid.rotation.x = -Math.PI / 2;
this.#scene.add(grid);
this.#terrain.push({ mesh: grid, material: gridMaterial });
}
#createPalmTrees() {
const createPalmTree = (x, z) => {
const trunkGeometry = new THREE.CylinderGeometry(2, 3, 30, 8);
const trunkMaterial = new THREE.MeshBasicMaterial({ color: 0x4a2f1b });
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.set(x, 15, z);
const leavesGeometry = new THREE.SphereGeometry(15, 8, 8);
const leavesMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 }
},
vertexShader: `
uniform float time;
varying vec3 vPosition;
void main() {
vPosition = position;
vec3 pos = position;
pos.x += sin(time + position.y * 0.5) * 2.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec3 vPosition;
void main() {
vec3 color = vec3(0.0, 0.5, 0.3);
gl_FragColor = vec4(color, 1.0);
}
`
});
const leaves = new THREE.Mesh(leavesGeometry, leavesMaterial);
leaves.position.y = 30;
trunk.add(leaves);
this.#scene.add(trunk);
this.#terrain.push({ mesh: leaves, material: leavesMaterial });
};
// Create palm trees at random positions
for (let i = 0; i < 20; i++) {
const x = Math.random() * 1000 - 500;
const z = Math.random() * 500 - 800;
createPalmTree(x, z);
}
}
#updateCamera() {
const moveSpeed = this.#moveSpeed;
const rotateSpeed = 0.002;
if (this.#keys['w']) this.#camera.translateZ(-moveSpeed);
if (this.#keys['s']) this.#camera.translateZ(moveSpeed);
if (this.#keys['a']) this.#camera.translateX(-moveSpeed);
if (this.#keys['d']) this.#camera.translateX(moveSpeed);
if (this.#keys[' ']) this.#camera.translateY(moveSpeed);
if (this.#keys['shift']) this.#camera.translateY(-moveSpeed);
this.#euler.y -= this.#mousePosition.x * rotateSpeed;
this.#euler.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.#euler.x - this.#mousePosition.y * rotateSpeed));
this.#camera.quaternion.setFromEuler(this.#euler);
this.#mousePosition.x = 0;
this.#mousePosition.y = 0;
}
#animate() {
requestAnimationFrame(() => this.#animate());
const time = this.#clock.getElapsedTime();
this.#updateCamera();
this.#terrain.forEach(({ material }) => {
if (material.uniforms) {
material.uniforms.time.value = time;
}
});
this.#renderer.render(this.#scene, this.#camera);
}
#setupEventListeners() {
document.addEventListener('keydown', (e) => this.#keys[e.key.toLowerCase()] = true);
document.addEventListener('keyup', (e) => this.#keys[e.key.toLowerCase()] = false);
document.addEventListener('mousemove', (e) => {
this.#mousePosition.x = e.movementX;
this.#mousePosition.y = e.movementY;
});
document.addEventListener('click', () => {
document.body.requestPointerLock();
});
window.addEventListener('resize', () => {
this.#camera.aspect = window.innerWidth / window.innerHeight;
this.#camera.updateProjectionMatrix();
this.#renderer.setSize(window.innerWidth, window.innerHeight);
});
}
#initDivineMessages() {
const messages = [
"GOD SAYS: HELLO WORLD",
"DIVINE INTERSECTION DETECTED",
"HOLY C COMPILATION SUCCESS",
"QUANTUM BITS ALIGNED",
"TEMPLE OS: GODS TEMPLE",
"CIA GLOWS IN THE DARK",
"16 COLOR DIVINE GRAPHICS",
"64 BIT RINGS OF SATURN"
];
const messageDiv = document.querySelector('.divine-messages');
setInterval(() => {
const randomMsg = messages[Math.floor(Math.random() * messages.length)];
messageDiv.textContent = randomMsg;
messageDiv.style.color = `hsl(${Math.random() * 360}, 100%, 50%)`;
}, 2000);
}
}
new VaporwaveParadise();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment