A browser source overlay for winter vibes for your Live Streams or Videos

initial commit

Trey Bastian 57a1cfb4

+24
.gitignore
···
+
# Logs
+
logs
+
*.log
+
npm-debug.log*
+
yarn-debug.log*
+
yarn-error.log*
+
pnpm-debug.log*
+
lerna-debug.log*
+
+
node_modules
+
dist
+
dist-ssr
+
*.local
+
+
# Editor directories and files
+
.vscode/*
+
!.vscode/extensions.json
+
.idea
+
.DS_Store
+
*.suo
+
*.ntvs*
+
*.njsproj
+
*.sln
+
*.sw?
+4
config.ts
···
+
export const CANVAS_WIDTH = 320;
+
export const CANVAS_HEIGHT = 180;
+
export const FLOOR_RAISE_THRESHOLD = 200;
+
export const MAX_PARTICAL_COUNT = 500;
+27
floor.ts
···
+
import { CANVAS_HEIGHT, CANVAS_WIDTH, FLOOR_RAISE_THRESHOLD } from "./config";
+
import Snowflake from "./snowflake";
+
+
export default class Floor {
+
y: number = CANVAS_HEIGHT;
+
accumulator = 0;
+
+
checkColission(flake: Snowflake): Boolean {
+
if (flake.y >= this.y) {
+
this.accumulator++;
+
+
if (this.accumulator % FLOOR_RAISE_THRESHOLD == 0) {
+
this.y -= 1;
+
console.log("floor raised");
+
}
+
return true;
+
}
+
return false;
+
}
+
+
draw(ctx: CanvasRenderingContext2D) {
+
ctx.beginPath();
+
ctx.rect(0, this.y, CANVAS_WIDTH, CANVAS_HEIGHT - this.y);
+
ctx.fillStyle = "#fff";
+
ctx.fill();
+
}
+
}
+13
index.html
···
+
<!doctype html>
+
<html lang="en">
+
<head>
+
<meta charset="UTF-8" />
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
<title>Winter Vibes</title>
+
</head>
+
<body>
+
<canvas id="main"></canvas>
+
<script type="module" src="/main.ts"></script>
+
</body>
+
</html>
+1
javascript.svg
···
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="#F7DF1E" d="M0 0h256v256H0V0Z"></path><path d="m67.312 213.932l19.59-11.856c3.78 6.701 7.218 12.371 15.465 12.371c7.905 0 12.89-3.092 12.89-15.12v-81.798h24.057v82.138c0 24.917-14.606 36.259-35.916 36.259c-19.245 0-30.416-9.967-36.087-21.996m85.07-2.576l19.588-11.341c5.157 8.421 11.859 14.607 23.715 14.607c9.969 0 16.325-4.984 16.325-11.858c0-8.248-6.53-11.17-17.528-15.98l-6.013-2.58c-17.357-7.387-28.87-16.667-28.87-36.257c0-18.044 13.747-31.792 35.228-31.792c15.294 0 26.292 5.328 34.196 19.247l-18.732 12.03c-4.125-7.389-8.591-10.31-15.465-10.31c-7.046 0-11.514 4.468-11.514 10.31c0 7.217 4.468 10.14 14.778 14.608l6.014 2.577c20.45 8.765 31.963 17.7 31.963 37.804c0 21.654-17.012 33.51-39.867 33.51c-22.339 0-36.774-10.654-43.819-24.574"></path></svg>
+46
main.ts
···
+
import "./style.css";
+
import Snowflake from "./snowflake";
+
import { CANVAS_HEIGHT, CANVAS_WIDTH, MAX_PARTICAL_COUNT } from "./config";
+
import Floor from "./floor";
+
+
let canvas: HTMLCanvasElement;
+
let context: CanvasRenderingContext2D;
+
let lastSpawn: DOMHighResTimeStamp = 0;
+
let floor = new Floor();
+
let lastFrame: number = 0;
+
let fps = 0;
+
const snowFlakes: Snowflake[] = [];
+
+
window.onload = init;
+
+
function init() {
+
canvas = document.getElementById("main") as HTMLCanvasElement;
+
context = canvas.getContext("2d")!;
+
canvas.width = CANVAS_WIDTH;
+
canvas.height = CANVAS_HEIGHT;
+
+
window.requestAnimationFrame(gameLoop);
+
}
+
+
function gameLoop(delta: DOMHighResTimeStamp) {
+
fps = Math.round(1 / ((delta - lastFrame) / 1000));
+
context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
+
if (delta - lastSpawn > 500 && snowFlakes.length < MAX_PARTICAL_COUNT) {
+
for (let i = 0; i < Math.floor(Math.random() * 2); i++) {
+
snowFlakes.push(new Snowflake());
+
}
+
}
+
+
snowFlakes.forEach((flake) => {
+
flake.move();
+
if (floor.checkColission(flake)) {
+
flake.reset();
+
}
+
flake.draw(context);
+
});
+
+
floor.draw(context);
+
context.fillText("FPS: " + fps, 10, 30);
+
lastFrame = delta;
+
window.requestAnimationFrame(gameLoop);
+
}
+15
package.json
···
+
{
+
"name": "wintervibesjs",
+
"private": true,
+
"version": "0.0.0",
+
"type": "module",
+
"scripts": {
+
"dev": "vite",
+
"build": "vite build",
+
"preview": "vite preview"
+
},
+
"devDependencies": {
+
"typescript": "^5.6.3",
+
"vite": "^5.4.10"
+
}
+
}
+518
pnpm-lock.yaml
···
+
lockfileVersion: '9.0'
+
+
settings:
+
autoInstallPeers: true
+
excludeLinksFromLockfile: false
+
+
importers:
+
+
.:
+
devDependencies:
+
typescript:
+
specifier: ^5.6.3
+
version: 5.6.3
+
vite:
+
specifier: ^5.4.10
+
version: 5.4.11
+
+
packages:
+
+
'@esbuild/aix-ppc64@0.21.5':
+
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+
engines: {node: '>=12'}
+
cpu: [ppc64]
+
os: [aix]
+
+
'@esbuild/android-arm64@0.21.5':
+
resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+
engines: {node: '>=12'}
+
cpu: [arm64]
+
os: [android]
+
+
'@esbuild/android-arm@0.21.5':
+
resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+
engines: {node: '>=12'}
+
cpu: [arm]
+
os: [android]
+
+
'@esbuild/android-x64@0.21.5':
+
resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [android]
+
+
'@esbuild/darwin-arm64@0.21.5':
+
resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+
engines: {node: '>=12'}
+
cpu: [arm64]
+
os: [darwin]
+
+
'@esbuild/darwin-x64@0.21.5':
+
resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [darwin]
+
+
'@esbuild/freebsd-arm64@0.21.5':
+
resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+
engines: {node: '>=12'}
+
cpu: [arm64]
+
os: [freebsd]
+
+
'@esbuild/freebsd-x64@0.21.5':
+
resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [freebsd]
+
+
'@esbuild/linux-arm64@0.21.5':
+
resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+
engines: {node: '>=12'}
+
cpu: [arm64]
+
os: [linux]
+
+
'@esbuild/linux-arm@0.21.5':
+
resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+
engines: {node: '>=12'}
+
cpu: [arm]
+
os: [linux]
+
+
'@esbuild/linux-ia32@0.21.5':
+
resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+
engines: {node: '>=12'}
+
cpu: [ia32]
+
os: [linux]
+
+
'@esbuild/linux-loong64@0.21.5':
+
resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+
engines: {node: '>=12'}
+
cpu: [loong64]
+
os: [linux]
+
+
'@esbuild/linux-mips64el@0.21.5':
+
resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+
engines: {node: '>=12'}
+
cpu: [mips64el]
+
os: [linux]
+
+
'@esbuild/linux-ppc64@0.21.5':
+
resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+
engines: {node: '>=12'}
+
cpu: [ppc64]
+
os: [linux]
+
+
'@esbuild/linux-riscv64@0.21.5':
+
resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+
engines: {node: '>=12'}
+
cpu: [riscv64]
+
os: [linux]
+
+
'@esbuild/linux-s390x@0.21.5':
+
resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+
engines: {node: '>=12'}
+
cpu: [s390x]
+
os: [linux]
+
+
'@esbuild/linux-x64@0.21.5':
+
resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [linux]
+
+
'@esbuild/netbsd-x64@0.21.5':
+
resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [netbsd]
+
+
'@esbuild/openbsd-x64@0.21.5':
+
resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [openbsd]
+
+
'@esbuild/sunos-x64@0.21.5':
+
resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [sunos]
+
+
'@esbuild/win32-arm64@0.21.5':
+
resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+
engines: {node: '>=12'}
+
cpu: [arm64]
+
os: [win32]
+
+
'@esbuild/win32-ia32@0.21.5':
+
resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+
engines: {node: '>=12'}
+
cpu: [ia32]
+
os: [win32]
+
+
'@esbuild/win32-x64@0.21.5':
+
resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+
engines: {node: '>=12'}
+
cpu: [x64]
+
os: [win32]
+
+
'@rollup/rollup-android-arm-eabi@4.27.3':
+
resolution: {integrity: sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==}
+
cpu: [arm]
+
os: [android]
+
+
'@rollup/rollup-android-arm64@4.27.3':
+
resolution: {integrity: sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==}
+
cpu: [arm64]
+
os: [android]
+
+
'@rollup/rollup-darwin-arm64@4.27.3':
+
resolution: {integrity: sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==}
+
cpu: [arm64]
+
os: [darwin]
+
+
'@rollup/rollup-darwin-x64@4.27.3':
+
resolution: {integrity: sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==}
+
cpu: [x64]
+
os: [darwin]
+
+
'@rollup/rollup-freebsd-arm64@4.27.3':
+
resolution: {integrity: sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==}
+
cpu: [arm64]
+
os: [freebsd]
+
+
'@rollup/rollup-freebsd-x64@4.27.3':
+
resolution: {integrity: sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==}
+
cpu: [x64]
+
os: [freebsd]
+
+
'@rollup/rollup-linux-arm-gnueabihf@4.27.3':
+
resolution: {integrity: sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==}
+
cpu: [arm]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm-musleabihf@4.27.3':
+
resolution: {integrity: sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==}
+
cpu: [arm]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm64-gnu@4.27.3':
+
resolution: {integrity: sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==}
+
cpu: [arm64]
+
os: [linux]
+
+
'@rollup/rollup-linux-arm64-musl@4.27.3':
+
resolution: {integrity: sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==}
+
cpu: [arm64]
+
os: [linux]
+
+
'@rollup/rollup-linux-powerpc64le-gnu@4.27.3':
+
resolution: {integrity: sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==}
+
cpu: [ppc64]
+
os: [linux]
+
+
'@rollup/rollup-linux-riscv64-gnu@4.27.3':
+
resolution: {integrity: sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==}
+
cpu: [riscv64]
+
os: [linux]
+
+
'@rollup/rollup-linux-s390x-gnu@4.27.3':
+
resolution: {integrity: sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==}
+
cpu: [s390x]
+
os: [linux]
+
+
'@rollup/rollup-linux-x64-gnu@4.27.3':
+
resolution: {integrity: sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==}
+
cpu: [x64]
+
os: [linux]
+
+
'@rollup/rollup-linux-x64-musl@4.27.3':
+
resolution: {integrity: sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==}
+
cpu: [x64]
+
os: [linux]
+
+
'@rollup/rollup-win32-arm64-msvc@4.27.3':
+
resolution: {integrity: sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==}
+
cpu: [arm64]
+
os: [win32]
+
+
'@rollup/rollup-win32-ia32-msvc@4.27.3':
+
resolution: {integrity: sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==}
+
cpu: [ia32]
+
os: [win32]
+
+
'@rollup/rollup-win32-x64-msvc@4.27.3':
+
resolution: {integrity: sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==}
+
cpu: [x64]
+
os: [win32]
+
+
'@types/estree@1.0.6':
+
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
+
esbuild@0.21.5:
+
resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+
engines: {node: '>=12'}
+
hasBin: true
+
+
fsevents@2.3.3:
+
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+
os: [darwin]
+
+
nanoid@3.3.7:
+
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+
hasBin: true
+
+
picocolors@1.1.1:
+
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+
postcss@8.4.49:
+
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
+
engines: {node: ^10 || ^12 || >=14}
+
+
rollup@4.27.3:
+
resolution: {integrity: sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==}
+
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+
hasBin: true
+
+
source-map-js@1.2.1:
+
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+
engines: {node: '>=0.10.0'}
+
+
typescript@5.6.3:
+
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
+
engines: {node: '>=14.17'}
+
hasBin: true
+
+
vite@5.4.11:
+
resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==}
+
engines: {node: ^18.0.0 || >=20.0.0}
+
hasBin: true
+
peerDependencies:
+
'@types/node': ^18.0.0 || >=20.0.0
+
less: '*'
+
lightningcss: ^1.21.0
+
sass: '*'
+
sass-embedded: '*'
+
stylus: '*'
+
sugarss: '*'
+
terser: ^5.4.0
+
peerDependenciesMeta:
+
'@types/node':
+
optional: true
+
less:
+
optional: true
+
lightningcss:
+
optional: true
+
sass:
+
optional: true
+
sass-embedded:
+
optional: true
+
stylus:
+
optional: true
+
sugarss:
+
optional: true
+
terser:
+
optional: true
+
+
snapshots:
+
+
'@esbuild/aix-ppc64@0.21.5':
+
optional: true
+
+
'@esbuild/android-arm64@0.21.5':
+
optional: true
+
+
'@esbuild/android-arm@0.21.5':
+
optional: true
+
+
'@esbuild/android-x64@0.21.5':
+
optional: true
+
+
'@esbuild/darwin-arm64@0.21.5':
+
optional: true
+
+
'@esbuild/darwin-x64@0.21.5':
+
optional: true
+
+
'@esbuild/freebsd-arm64@0.21.5':
+
optional: true
+
+
'@esbuild/freebsd-x64@0.21.5':
+
optional: true
+
+
'@esbuild/linux-arm64@0.21.5':
+
optional: true
+
+
'@esbuild/linux-arm@0.21.5':
+
optional: true
+
+
'@esbuild/linux-ia32@0.21.5':
+
optional: true
+
+
'@esbuild/linux-loong64@0.21.5':
+
optional: true
+
+
'@esbuild/linux-mips64el@0.21.5':
+
optional: true
+
+
'@esbuild/linux-ppc64@0.21.5':
+
optional: true
+
+
'@esbuild/linux-riscv64@0.21.5':
+
optional: true
+
+
'@esbuild/linux-s390x@0.21.5':
+
optional: true
+
+
'@esbuild/linux-x64@0.21.5':
+
optional: true
+
+
'@esbuild/netbsd-x64@0.21.5':
+
optional: true
+
+
'@esbuild/openbsd-x64@0.21.5':
+
optional: true
+
+
'@esbuild/sunos-x64@0.21.5':
+
optional: true
+
+
'@esbuild/win32-arm64@0.21.5':
+
optional: true
+
+
'@esbuild/win32-ia32@0.21.5':
+
optional: true
+
+
'@esbuild/win32-x64@0.21.5':
+
optional: true
+
+
'@rollup/rollup-android-arm-eabi@4.27.3':
+
optional: true
+
+
'@rollup/rollup-android-arm64@4.27.3':
+
optional: true
+
+
'@rollup/rollup-darwin-arm64@4.27.3':
+
optional: true
+
+
'@rollup/rollup-darwin-x64@4.27.3':
+
optional: true
+
+
'@rollup/rollup-freebsd-arm64@4.27.3':
+
optional: true
+
+
'@rollup/rollup-freebsd-x64@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-arm-gnueabihf@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-arm-musleabihf@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-arm64-gnu@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-arm64-musl@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-powerpc64le-gnu@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-riscv64-gnu@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-s390x-gnu@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-x64-gnu@4.27.3':
+
optional: true
+
+
'@rollup/rollup-linux-x64-musl@4.27.3':
+
optional: true
+
+
'@rollup/rollup-win32-arm64-msvc@4.27.3':
+
optional: true
+
+
'@rollup/rollup-win32-ia32-msvc@4.27.3':
+
optional: true
+
+
'@rollup/rollup-win32-x64-msvc@4.27.3':
+
optional: true
+
+
'@types/estree@1.0.6': {}
+
+
esbuild@0.21.5:
+
optionalDependencies:
+
'@esbuild/aix-ppc64': 0.21.5
+
'@esbuild/android-arm': 0.21.5
+
'@esbuild/android-arm64': 0.21.5
+
'@esbuild/android-x64': 0.21.5
+
'@esbuild/darwin-arm64': 0.21.5
+
'@esbuild/darwin-x64': 0.21.5
+
'@esbuild/freebsd-arm64': 0.21.5
+
'@esbuild/freebsd-x64': 0.21.5
+
'@esbuild/linux-arm': 0.21.5
+
'@esbuild/linux-arm64': 0.21.5
+
'@esbuild/linux-ia32': 0.21.5
+
'@esbuild/linux-loong64': 0.21.5
+
'@esbuild/linux-mips64el': 0.21.5
+
'@esbuild/linux-ppc64': 0.21.5
+
'@esbuild/linux-riscv64': 0.21.5
+
'@esbuild/linux-s390x': 0.21.5
+
'@esbuild/linux-x64': 0.21.5
+
'@esbuild/netbsd-x64': 0.21.5
+
'@esbuild/openbsd-x64': 0.21.5
+
'@esbuild/sunos-x64': 0.21.5
+
'@esbuild/win32-arm64': 0.21.5
+
'@esbuild/win32-ia32': 0.21.5
+
'@esbuild/win32-x64': 0.21.5
+
+
fsevents@2.3.3:
+
optional: true
+
+
nanoid@3.3.7: {}
+
+
picocolors@1.1.1: {}
+
+
postcss@8.4.49:
+
dependencies:
+
nanoid: 3.3.7
+
picocolors: 1.1.1
+
source-map-js: 1.2.1
+
+
rollup@4.27.3:
+
dependencies:
+
'@types/estree': 1.0.6
+
optionalDependencies:
+
'@rollup/rollup-android-arm-eabi': 4.27.3
+
'@rollup/rollup-android-arm64': 4.27.3
+
'@rollup/rollup-darwin-arm64': 4.27.3
+
'@rollup/rollup-darwin-x64': 4.27.3
+
'@rollup/rollup-freebsd-arm64': 4.27.3
+
'@rollup/rollup-freebsd-x64': 4.27.3
+
'@rollup/rollup-linux-arm-gnueabihf': 4.27.3
+
'@rollup/rollup-linux-arm-musleabihf': 4.27.3
+
'@rollup/rollup-linux-arm64-gnu': 4.27.3
+
'@rollup/rollup-linux-arm64-musl': 4.27.3
+
'@rollup/rollup-linux-powerpc64le-gnu': 4.27.3
+
'@rollup/rollup-linux-riscv64-gnu': 4.27.3
+
'@rollup/rollup-linux-s390x-gnu': 4.27.3
+
'@rollup/rollup-linux-x64-gnu': 4.27.3
+
'@rollup/rollup-linux-x64-musl': 4.27.3
+
'@rollup/rollup-win32-arm64-msvc': 4.27.3
+
'@rollup/rollup-win32-ia32-msvc': 4.27.3
+
'@rollup/rollup-win32-x64-msvc': 4.27.3
+
fsevents: 2.3.3
+
+
source-map-js@1.2.1: {}
+
+
typescript@5.6.3: {}
+
+
vite@5.4.11:
+
dependencies:
+
esbuild: 0.21.5
+
postcss: 8.4.49
+
rollup: 4.27.3
+
optionalDependencies:
+
fsevents: 2.3.3
+1
public/vite.svg
···
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
+42
snowflake.ts
···
+
import { CANVAS_WIDTH } from "./config";
+
+
export default class Snowflake {
+
x: number;
+
y: number;
+
size: number;
+
speed: number;
+
+
constructor() {
+
this.x = Math.floor(Math.random() * CANVAS_WIDTH);
+
this.y = -1;
+
this.speed = Math.random();
+
this.size = Math.floor(Math.random() * 4);
+
}
+
+
reset() {
+
this.x = Math.floor(Math.random() * CANVAS_WIDTH);
+
this.y = -1;
+
this.speed = Math.random();
+
this.size = Math.floor(Math.random() * 4);
+
}
+
+
draw(context: CanvasRenderingContext2D) {
+
context.beginPath();
+
context.rect(this.x, this.y, this.size, this.size);
+
context.fillStyle = "#fff";
+
context.fill();
+
}
+
+
move() {
+
this.y += this.speed;
+
const sway = Math.random() < 0.5;
+
if (sway) {
+
const direction = Math.random() < 0.5;
+
if (direction) {
+
this.x += (this.speed * 5) / 10;
+
} else {
+
this.x -= (this.speed * 5) / 10;
+
}
+
}
+
}
+
}
+22
style.css
···
+
*,
+
*:after,
+
*:befpre {
+
box-sizing: border-box;
+
}
+
+
body {
+
background-color: #091e36;
+
min-width: 100vw;
+
min-height: 100vh;
+
height: 100%;
+
width: 100%;
+
margin: 0;
+
padding: 0;
+
}
+
+
canvas {
+
background-color: transparent;
+
width: 100%;
+
height: 100%;
+
image-rendering: pixelated;
+
}
+110
tsconfig.json
···
+
{
+
"compilerOptions": {
+
/* Visit https://aka.ms/tsconfig to read more about this file */
+
+
/* Projects */
+
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+
/* Language and Environment */
+
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+
// "jsx": "preserve", /* Specify what JSX code is generated. */
+
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
+
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+
/* Modules */
+
"module": "commonjs", /* Specify what module code is generated. */
+
// "rootDir": "./", /* Specify the root folder within your source files. */
+
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
+
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
+
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
+
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
+
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
+
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
+
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
+
// "resolveJsonModule": true, /* Enable importing .json files. */
+
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
+
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+
/* JavaScript Support */
+
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+
/* Emit */
+
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
+
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+
// "noEmit": true, /* Disable emitting files from a compilation. */
+
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+
// "outDir": "./", /* Specify an output folder for all emitted files. */
+
// "removeComments": true, /* Disable emitting comments. */
+
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+
// "newLine": "crlf", /* Set the newline character for emitting files. */
+
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+
+
/* Interop Constraints */
+
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
+
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
+
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+
/* Type Checking */
+
"strict": true, /* Enable all strict type-checking options. */
+
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
+
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+
/* Completeness */
+
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
+
}
+
}