{"slug": "a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window", "title": "a pure css implementation of some sunlight streaming in through the window", "summary": "A developer built a pure CSS sunlight effect using layered divs, backdrop-filter blur, and SVG filters, inspired by Daylight Computer and Sunlit Place. The implementation uses multiple blur layers with mask images for progressive depth, and feTurbulence with feDisplacementMap to animate leaves billowing. The project also includes a sunrise/sunset color transition and a perspective transform matrix for realistic distortion.", "body_md": "## Screen.Recording.2024-11-11.at.1.49.49.PM.mov\n\ninspired by [https://daylightcomputer.com/](https://daylightcomputer.com/) and [https://www.sunlit.place/](https://www.sunlit.place/)\n\n- this is all just divs with background\n- the leaves i sourced from adobe stock photos and downscaled to ~300x500 because i knew i would be blurring it anyways\n\n```\n<div id=\"blinds\">\n<div class=\"shutters\">\n  <div class=\"shutter\"></div>\n  ...\n  <div class=\"shutter\"></div>\n</div>\n<div class=\"vertical\">\n  <div class=\"bar\"></div>\n  <div class=\"bar\"></div>\n</div>\n</div>\n```\n\n- gotta make the diffusion effect convincing\n- things closer to the wall should be less blurry than things away from the wall\n- this utilizes multiple blur layers each with a mask image to constraint its effect zone\n\n```\n#progressive-blur {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n}\n\n#progressive-blur>div {\n  position: absolute;\n  height: 100%;\n  width: 100%;\n  inset: 0;\n  backdrop-filter: blur(var(--blur-amount));\n  mask-image: linear-gradient(252deg, transparent, transparent var(--stop1), black var(--stop2), black);\n}\n\n#progressive-blur>div:nth-child(1) {\n  --blur-amount: 6px;\n  --stop1: 0%;\n  --stop2: 0%;\n}\n\n#progressive-blur>div:nth-child(2) {\n  --blur-amount: 12px;\n  --stop1: 40%;\n  --stop2: 80%;\n}\n\n#progressive-blur>div:nth-child(3) {\n  --blur-amount: 48px;\n  --stop1: 40%;\n  --stop2: 70%;\n}\n\n#progressive-blur>div:nth-child(4) {\n  --blur-amount: 96px;\n  --stop1: 70%;\n  --stop2: 80%;\n}\n```\n\n- created a leaf billowing effect using small amounts of rotate and scale\n- to add a bit more dynamism, i used svg filters and the lesser known\n`feTurbulence`\n\nand`feDisplacementMap`\n\ntags to add a higher octave of noise to billow individual leaves\n\n```\n  <div id=\"leaves\">\n    <svg style=\"width: 0; height: 0; position: absolute;\">\n      <defs>\n        <filter id=\"wind\" x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">\n          <feTurbulence type=\"fractalNoise\" numOctaves=\"2\" seed=\"1\">\n            <animate attributeName=\"baseFrequency\" dur=\"16s\" keyTimes=\"0;0.33;0.66;1\"\n              values=\"0.005 0.003;0.01 0.009;0.008 0.004;0.005 0.003\" repeatCount=\"indefinite\" />\n          </feTurbulence>\n          <feDisplacementMap in=\"SourceGraphic\">\n            <animate attributeName=\"scale\" dur=\"20s\" keyTimes=\"0;0.25;0.5;0.75;1\" values=\"45;55;75;55;45\"\n              repeatCount=\"indefinite\" />\n          </feDisplacementMap>\n        </filter>\n      </defs>\n    </svg>\n  </div>\n#leaves {\n  position: absolute;\n  background-size: cover;\n  background-repeat: no-repeat;\n  bottom: -20px;\n  right: -700px;\n  width: 1600px;\n  height: 1400px;\n  background-image: url(\"./leaves.png\");\n  filter: url(#wind);\n  animation: billow 8s ease-in-out infinite;\n}\n\n@keyframes billow {\n  0% {\n    transform: perspective(400px) rotateX(0deg) rotateY(0deg) scale(1);\n  }\n\n  25% {\n    transform: perspective(400px) rotateX(1deg) rotateY(2deg) scale(1.02);\n  }\n\n  50% {\n    transform: perspective(400px) rotateX(-4deg) rotateY(-2deg) scale(0.97);\n  }\n\n  75% {\n    transform: perspective(400px) rotateX(1deg) rotateY(-1deg) scale(1.04);\n  }\n\n  100% {\n    transform: perspective(400px) rotateX(0deg) rotateY(0deg) scale(1);\n  }\n}\n```\n\n## Screen.Recording.2024-11-11.at.2.00.38.PM.mov\n\n- the light/dark transition was a bit too abrupt so i decided to add a transition between the two\n- a linear interpolation of colors wasn't super exciting so I decided to create a sunrise/sunset color animation\n- these colors are just handpicked from many hours of just looking at sunsets and some experimentation, nothing too fancy here\n- theres also a subtle 'glow' that is a very crude approximation of light bouncing off the floor\n\n- finally, skew + rotate + transform didnt get what i wanted because it only gives you affine transforms and i want that nice perspective distortion\n- decided to derive a transform matrix for memes, this involved pulling desmos to draw some shapes\n- then, we can solve for a 4d perspective transform matrix that\n`matrix3d`\n\ncan use in css\n\n`matrix3d`\n\nvalues are calculated using the following python script:\n\n``` python\nimport numpy as np\n\ndef compute_homography(points_src, points_dst):\n    if points_src.shape != (4, 2) or points_dst.shape != (4, 2):\n        raise ValueError(\"Input arrays must be of shape (4,2)\")\n\n    A = np.zeros((8, 8))\n    b = np.zeros(8)\n\n    for i in range(4):\n        x, y = points_src[i]\n        x_prime, y_prime = points_dst[i]\n        A[i * 2] = [x, y, 1, 0, 0, 0, -x * x_prime, -y * x_prime]\n        b[i * 2] = x_prime\n        A[i * 2 + 1] = [0, 0, 0, x, y, 1, -x * y_prime, -y * y_prime]\n        b[i * 2 + 1] = y_prime\n\n    h = np.linalg.solve(A, b)\n    H = np.array([\n        [h[0], h[1], 0, h[2]],\n        [h[3], h[4], 0, h[5]],\n        [0.0, 0.0, 1.0, 0.0],\n        [h[6], h[7], 0.0, 1.0]\n    ])\n\n    return H\n\nif __name__ == \"__main__\":\n    src = np.array([[-1, 0], [0, 0], [0, -1], [-1, -1]]) * 500\n    # day\n    dst = np.array([[-1, -0.1], [0, 0], [0, -1], [-1, -1.7]]) * 500\n    # night\n    # dst = np.array([[-1, 0.1], [0, 0], [0, -1], [-1, -1.1]]) * 500\n\n    # Flip y-axis in src and dst\n    src[:, 1] = -src[:, 1]\n    dst[:, 1] = -dst[:, 1]\n\n    H = compute_homography(src, dst)\n    print(\"Homography matrix:\")\n    print(H)\n\n    print(\"Homography matrix as matrix3d in column-major order:\")\n    print(\"transform: matrix3d(\")\n    for col in range(4):\n        values = \", \".join(f\"{H[row, col]:.4f}\" for row in range(4))\n        print(f\"    {values},\" if col < 3 else f\"    {values}\")\n    print(\");\")\n```\n\n", "url": "https://wpnews.pro/news/a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window", "canonical_source": "https://github.com/jackyzha0/sunlit", "published_at": "2026-08-10 15:29:34+00:00", "updated_at": "2026-08-10 16:06:43.029949+00:00", "lang": "en", "topics": ["developer-tools", "generative-ai"], "entities": ["Daylight Computer", "Sunlit Place", "Adobe Stock"], "alternates": {"html": "https://wpnews.pro/news/a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window", "markdown": "https://wpnews.pro/news/a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window.md", "text": "https://wpnews.pro/news/a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window.txt", "jsonld": "https://wpnews.pro/news/a-pure-css-implementation-of-some-sunlight-streaming-in-through-the-window.jsonld"}}