Last active
December 11, 2024 07:02
-
-
Save bosley/d06e72e6af5e0f4567ebc90bde6707ba to your computer and use it in GitHub Desktop.
SSG 3D Vaporware adventure by Claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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