JSFiddle - React, Tailwind, and code Playground

by Craig Haywood

HTML

<!-- This was made for the codepen challege however is using a custom
made SVG which I have imported into the CSS using background-image.I have
another pen for the challenge using js only https://codepen.io/thomasbutler/pen/MWMVeoW
-->
<head>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Raleway+Dots&display=swap"
  rel="stylesheet">
</head>
<body>
  <div id="fractal-container">
    <div class="layer" id="layer1">
    </div>
    <div class="layer" id="layer2">
    </div>
    <div class="layer" id="layer3">
    </div>
  </div>
  <div id="controls">
    Feel like you are in a death spiral?<br/>We can help<br/><br/><img width="24" height="24" src="https://www.secondsplash.com/pointer.png"/>
  </div>

CSS

body, html {
  margin: 0;
  padding: 0;
  height: 100%;
  overflow: hidden;
  font-family: "Raleway Dots", sans-serif;
  font-weight: 400;
  font-style: normal;
  background: linear-gradient(
    90deg,#ff0000,#ff7f00,#ffff00,#7fff00,#00ff00,
    #00ffff,#007fff,#0000ff,#7f00ff,#ff00ff
  );
}

#fractal-container {
  position: relative;
  width: 100vw;
  height: 100vh;
  perspective: 1000px;
}

.layer {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: url("https://assets.codepen.io/11761718/LN13ya01.svg");
  background-size: cover;
  background-position: center;
  opacity: 0.7;
  mix-blend-mode: screen;
  transition: transform 0.1s ease-out;
}

#layer1 { filter: hue-rotate(0deg); }
#layer2 { filter: hue-rotate(120deg); }
#layer3 { filter: hue-rotate(240deg); }

#controls {
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 1);
  padding: 15px;
  border-radius: 15px;
  display: flex;
  align-items: center;
  z-index: 1000;
  color: white;
  text-align: center;
  font-weight: bold
}

input[type="range"] {
  -webkit-appearance: none;
  width: 150px;
  height: 5px;
  background: rgba(255, 255, 255, 0.3);
  outline: none;
  border-radius: 5px;
  margin: 0 10px;
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  background: #00ffaa;
  cursor: pointer;
  border-radius: 50%;
}

input[type="range"]::-moz-range-thumb {
  width: 15px;
  height: 15px;
  background: #00ffaa;
  cursor: pointer;
  border-radius: 50%;
}

label {
  color: #00ffaa;
  margin-right: 20px;
  font-weight: 400;
  font-style: normal;
  text-shadow: 0 0 5px #00ffaa;
}

/* Media Queries */
@media (max-width: 1024px) {
  #controls {
    flex-direction: column;
    align-items: stretch;
    width: 90%;
    max-width: 350px;
    background: rgba(0, 0, 0, 0.8);
  }
  
  input[type="range"] {
    width: 100%;
    margin: 10px 0;
  }
  
...

JavaScript

let zoomLevel = 1;
let animationSpeed = 1;
let distortionLevel = 0;

function init() {
    
    
    
   
    window.addEventListener('resize', adjustForScreenSize);
    adjustForScreenSize();
    animate();
}

function updateZoom() {
    zoomLevel = 1;
}

function updateSpeed() {
    animationSpeed = 1;
}

function updateDistortion() {
    distortionLevel = 0;
}

function handleMouseMove(e) {
    const x = e.clientX / window.innerWidth - 0.5;
    const y = e.clientY / window.innerHeight - 0.5;
    rotateFractal(x, y);
}

function handleTouchMove(e) {
    const touch = e.touches[0];
    const x = touch.clientX / window.innerWidth - 0.5;
    const y = touch.clientY / window.innerHeight - 0.5;
    rotateFractal(x, y);
}

function rotateFractal(x, y) {
    const container = document.getElementById('fractal-container');
    container.style.transform = `rotateY(${x * 20}deg) rotateX(${-y * 20}deg)`;
}

function adjustForScreenSize() {
    const container = document.getElementById('fractal-container');
    const size = Math.max(window.innerWidth, window.innerHeight);
    container.style.width = `${size}px`;
    container.style.height = `${size}px`;
    container.style.left = `${(window.innerWidth - size) / 2}px`;
    container.style.top = `${(window.innerHeight - size) / 2}px`;
}

function animate() {
    const layers = document.querySelectorAll('.layer');
    const time = Date.now() * 0.001 * animationSpeed;
    
    layers.forEach((layer, index) => {
        const scale = zoomLevel * (1 + Math.sin(time + index) * 0.1);
        const rotate = Math.sin(time * 0.5 + index) * 10;
        const translateX = Math.sin(time * 0.7 + index) * distortionLevel;
        const translateY = Math.cos(time * 0.7 + index) * distortionLevel;
        
        layer.style.transform = `scale(${scale}) rotate(${rotate}deg) translate(${translateX}px, ${translateY}px)`;
    });

    requestAnimationFrame(animate);
}

window.addEventListener('load', init);