From 5e8ff175654bb5841f2217f627c94ca1d386bf45 Mon Sep 17 00:00:00 2001 From: Szymon Szulc Date: Mon, 25 May 2026 23:52:47 +0200 Subject: [PATCH 1/5] postmortem changes --- .../typegpu-cli/templates/template-vite-simple/oxlint.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts b/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts index 7b676ba672..73a3a0e361 100644 --- a/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts +++ b/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts @@ -12,7 +12,6 @@ export default defineConfig({ ...typegpu.configs.recommended.rules, 'typescript/no-non-null-assertion': 'error', 'typescript/no-explicit-any': 'error', - 'typescript/no-unsafe-type-assertion': 'off', 'import/no-named-as-default': 'off', }, env: { From aa469d9831880afd15c0b67594ff5877eec3334e Mon Sep 17 00:00:00 2001 From: Szymon Szulc Date: Tue, 26 May 2026 15:20:21 +0200 Subject: [PATCH 2/5] oxlint no unsafe casts --- .../typegpu-cli/templates/template-vite-simple/oxlint.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts b/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts index 73a3a0e361..7b676ba672 100644 --- a/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts +++ b/packages/typegpu-cli/templates/template-vite-simple/oxlint.config.ts @@ -12,6 +12,7 @@ export default defineConfig({ ...typegpu.configs.recommended.rules, 'typescript/no-non-null-assertion': 'error', 'typescript/no-explicit-any': 'error', + 'typescript/no-unsafe-type-assertion': 'off', 'import/no-named-as-default': 'off', }, env: { From 62a1a77b6df714548d6a8db361cba64a78127390 Mon Sep 17 00:00:00 2001 From: Szymon Szulc Date: Tue, 26 May 2026 13:40:48 +0200 Subject: [PATCH 3/5] domain warp template --- packages/typegpu-cli/src/create.ts | 4 + .../template-vite-complex/_gitignore | 22 ++ .../templates/template-vite-complex/_nvmrc | 1 + .../template-vite-complex/_oxfmtrc.json | 3 + .../template-vite-complex/_package.json | 36 ++ .../_vscode/settings.json | 5 + .../template-vite-complex/_zed/settings.json | 15 + .../template-vite-complex/index.html | 102 ++++++ .../template-vite-complex/oxlint.config.ts | 21 ++ .../template-vite-complex/public/favicon.svg | 16 + .../template-vite-complex/public/icons.svg | 16 + .../public/typegpu-logo-dark.svg | 59 ++++ .../public/typegpu-logo-light.svg | 58 ++++ .../template-vite-complex/public/vite.svg | 1 + .../template-vite-complex/src/main.ts | 146 ++++++++ .../template-vite-complex/src/style.css | 315 ++++++++++++++++++ .../template-vite-complex/tsconfig.json | 21 ++ .../template-vite-complex/vite.config.ts | 6 + 18 files changed, 847 insertions(+) create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_gitignore create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_nvmrc create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_oxfmtrc.json create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_package.json create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_vscode/settings.json create mode 100644 packages/typegpu-cli/templates/template-vite-complex/_zed/settings.json create mode 100644 packages/typegpu-cli/templates/template-vite-complex/index.html create mode 100644 packages/typegpu-cli/templates/template-vite-complex/oxlint.config.ts create mode 100644 packages/typegpu-cli/templates/template-vite-complex/public/favicon.svg create mode 100644 packages/typegpu-cli/templates/template-vite-complex/public/icons.svg create mode 100644 packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-dark.svg create mode 100644 packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-light.svg create mode 100644 packages/typegpu-cli/templates/template-vite-complex/public/vite.svg create mode 100644 packages/typegpu-cli/templates/template-vite-complex/src/main.ts create mode 100644 packages/typegpu-cli/templates/template-vite-complex/src/style.css create mode 100644 packages/typegpu-cli/templates/template-vite-complex/tsconfig.json create mode 100644 packages/typegpu-cli/templates/template-vite-complex/vite.config.ts diff --git a/packages/typegpu-cli/src/create.ts b/packages/typegpu-cli/src/create.ts index 305ceb4227..4036a6d11c 100644 --- a/packages/typegpu-cli/src/create.ts +++ b/packages/typegpu-cli/src/create.ts @@ -14,6 +14,10 @@ const PROJECT_TEMPLATES = [ value: 'vite-simple', label: rgbText('Vite (Simple)', 175, 105, 245), }, + { + value: 'vite-complex', + label: rgbText('Vite (Complex)', 169, 161, 244), + }, ]; export async function createProject(cwd: string) { diff --git a/packages/typegpu-cli/templates/template-vite-complex/_gitignore b/packages/typegpu-cli/templates/template-vite-complex/_gitignore new file mode 100644 index 0000000000..8b7e50214d --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_gitignore @@ -0,0 +1,22 @@ +# 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 +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/typegpu-cli/templates/template-vite-complex/_nvmrc b/packages/typegpu-cli/templates/template-vite-complex/_nvmrc new file mode 100644 index 0000000000..a45fd52cc5 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_nvmrc @@ -0,0 +1 @@ +24 diff --git a/packages/typegpu-cli/templates/template-vite-complex/_oxfmtrc.json b/packages/typegpu-cli/templates/template-vite-complex/_oxfmtrc.json new file mode 100644 index 0000000000..544138be45 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/_package.json b/packages/typegpu-cli/templates/template-vite-complex/_package.json new file mode 100644 index 0000000000..e40bfa0590 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_package.json @@ -0,0 +1,36 @@ +{ + "name": "typegpu-vanilla-vite-simple-project", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "check": "oxlint && oxfmt --check", + "fix": "oxlint --fix && oxfmt", + "types": "tsc --p ./tsconfig.json --noEmit" + }, + "dependencies": { + "@typegpu/color": "^0.11.0", + "@typegpu/noise": "^0.11.0", + "typegpu": "^0.11.6" + }, + "devDependencies": { + "@webgpu/types": "^0.1.70", + "eslint-plugin-typegpu": "^0.11.1", + "oxfmt": "^0.49.0", + "oxlint": "^1.64.0", + "typescript": "npm:tsover@6.0.1", + "unplugin-typegpu": "^0.11.4", + "vite": "^8.0.12" + }, + "overrides": { + "typescript": "npm:tsover@6.0.1" + }, + "pnpm": { + "overrides": { + "typescript": "npm:tsover@6.0.1" + } + } +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/_vscode/settings.json b/packages/typegpu-cli/templates/template-vite-complex/_vscode/settings.json new file mode 100644 index 0000000000..0dfacd95d2 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_vscode/settings.json @@ -0,0 +1,5 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.preferences.importModuleSpecifier": "relative", + "typescript.enablePromptUseWorkspaceTsdk": true +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/_zed/settings.json b/packages/typegpu-cli/templates/template-vite-complex/_zed/settings.json new file mode 100644 index 0000000000..9d7badfbd2 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/_zed/settings.json @@ -0,0 +1,15 @@ +// Folder-specific settings +// +// For a full list of overridable settings, and general information on folder-specific settings, +// see the documentation: https://zed.dev/docs/configuring-zed#settings-files +{ + "lsp": { + "vtsls": { + "settings": { + "typescript": { + "tsdk": "node_modules/typescript/lib" + } + } + } + } +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/index.html b/packages/typegpu-cli/templates/template-vite-complex/index.html new file mode 100644 index 0000000000..3edee33107 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/index.html @@ -0,0 +1,102 @@ + + + + + + + + typegpu-vanilla-vite-project + + +
+
+ + +
+ +
+ +
+
+ + + + + +

Type-safe WebGPU

+ +
+
+

+ Vite + +

+

Next generation frontend tooling

+ +
+
+ +
+
+
+ + + diff --git a/packages/typegpu-cli/templates/template-vite-complex/oxlint.config.ts b/packages/typegpu-cli/templates/template-vite-complex/oxlint.config.ts new file mode 100644 index 0000000000..7b676ba672 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/oxlint.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'oxlint'; +import typegpu from 'eslint-plugin-typegpu'; + +export default defineConfig({ + plugins: ['typescript', 'import', 'unicorn', 'oxc'], + jsPlugins: ['eslint-plugin-typegpu'], + categories: { + correctness: 'warn', + suspicious: 'warn', + }, + rules: { + ...typegpu.configs.recommended.rules, + 'typescript/no-non-null-assertion': 'error', + 'typescript/no-explicit-any': 'error', + 'typescript/no-unsafe-type-assertion': 'off', + 'import/no-named-as-default': 'off', + }, + env: { + builtin: true, + }, +}); diff --git a/packages/typegpu-cli/templates/template-vite-complex/public/favicon.svg b/packages/typegpu-cli/templates/template-vite-complex/public/favicon.svg new file mode 100644 index 0000000000..8b5e90d1a9 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/public/favicon.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/typegpu-cli/templates/template-vite-complex/public/icons.svg b/packages/typegpu-cli/templates/template-vite-complex/public/icons.svg new file mode 100644 index 0000000000..b6755b1121 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/public/icons.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-dark.svg b/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-dark.svg new file mode 100644 index 0000000000..65c9a02c31 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-dark.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-light.svg b/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-light.svg new file mode 100644 index 0000000000..a1b63335fc --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/public/typegpu-logo-light.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/typegpu-cli/templates/template-vite-complex/public/vite.svg b/packages/typegpu-cli/templates/template-vite-complex/public/vite.svg new file mode 100644 index 0000000000..5101b674df --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/public/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/packages/typegpu-cli/templates/template-vite-complex/src/main.ts b/packages/typegpu-cli/templates/template-vite-complex/src/main.ts new file mode 100644 index 0000000000..f89fa831eb --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/src/main.ts @@ -0,0 +1,146 @@ +const slider = document.querySelector('#slider') as HTMLInputElement; +let speed = Number(slider.value); +slider.addEventListener('input', () => { + speed = Number(slider.value); +}); + +import tgpu, { common, d, std } from 'typegpu'; +import { hexToOklab, oklabToRgb } from '@typegpu/color'; +import { perlin2d } from '@typegpu/noise'; + +const root = await tgpu.init(); +const time = root.createUniform(d.f32); + +const canvas = document.querySelector('#canvas') as HTMLCanvasElement; + +const context = root.configureContext({ canvas }); + +const noise = (v: d.v2f) => { + 'use gpu'; + return perlin2d.sample(v); +}; + +const octavesAccessor = tgpu.accessor(d.u32); +const standardizeNoiseAccessor = tgpu.accessor(d.bool); +const getMaxValue = tgpu.comptime((octaves: number) => 1 - 2 ** -octaves); +const rotation = d.mat2x2f(0.8, 0.6, -0.6, 0.8); + +const fbm = tgpu.fn((v: d.v2f) => { + 'use gpu'; + let u = d.vec2f(v); + let f = d.f32(); + + // first octave + { + let sample = noise(u + time.$); + if (standardizeNoiseAccessor.$) sample = sample * 0.5 + 0.5; + f += 0.5 * sample; + u = rotation * u * 2.01; + } + + for (const i of tgpu.unroll(std.range(2, octavesAccessor.$))) { + let sample = noise(u); + if (standardizeNoiseAccessor.$) sample = sample * 0.5 + 0.5; + f += 0.5 ** i * sample; + u = rotation * u * (2 + i / 100); + } + + // last octave + { + let sample = noise(u + std.sin(time.$)); + if (standardizeNoiseAccessor.$) sample = sample * 0.5 + 0.5; + f += 0.5 ** d.f32(octavesAccessor.$) * sample; + u = rotation * u * 2.01; + } + + return f / getMaxValue(octavesAccessor.$); +}); + +const fbm4 = fbm.with(octavesAccessor, 4).with(standardizeNoiseAccessor, false); +const fbm6 = fbm.with(octavesAccessor, 6).with(standardizeNoiseAccessor, true); + +const domainWarp = (v: d.v2f) => { + 'use gpu'; + return fbm6(v + fbm4(v + fbm4(v))); +}; + +const palette = (t: number) => { + 'use gpu'; + const purple = hexToOklab('#c04bf2'); + const blue = hexToOklab('#4e65f6'); + const dark = hexToOklab('#0f092b'); + + const factor1 = std.smoothstep(0.25, 0.5, t); + + const factor2 = std.smoothstep(0.5, 0.7, t); + + const mixed = std.mix(std.mix(dark, blue, factor1), purple, factor2); + + return oklabToRgb(mixed); +}; + +const VIRTUAL_GRID_SIZE = 4; +const EPS = 0.04; +const DIFF_SCALE = 0.4; +const pipeline = root.createRenderPipeline({ + vertex: common.fullScreenTriangle, + fragment: ({ uv }) => { + 'use gpu'; + const centeredUV = (2 * uv - 1) * VIRTUAL_GRID_SIZE; + + const sample = domainWarp(centeredUV); + const sampleRight = domainWarp(centeredUV + d.vec2f(EPS, 0)); + const sampleDown = domainWarp(centeredUV + d.vec2f(0, EPS)); + + const dx = d.vec3f(EPS, 0, (sampleRight - sample) * DIFF_SCALE); + const dy = d.vec3f(0, EPS, (sampleDown - sample) * DIFF_SCALE); + + const normal = std.normalize(std.cross(dx, dy)); + + const lightDir = std.normalize(d.vec3f(0.0, -1.0, 1.0)); + const diffuse = std.max(0.0, std.dot(normal, lightDir)); + + const baseColor = palette(sample); + + const litColor = baseColor * diffuse; + + return d.vec4f(litColor, 1); + }, +}); + +let elapsed = 0; +let lastTimestamp: number | null = null; + +function render(timestamp: number) { + if (lastTimestamp !== null) { + elapsed += ((timestamp - lastTimestamp) / 1000.0) * speed; + } + + lastTimestamp = timestamp; + time.write(elapsed); + + pipeline.withColorAttachment({ view: context }).draw(3); + + requestAnimationFrame(render); +} + +const observer = new ResizeObserver(([entry]) => { + if (!entry) { + return; + } + const width = + entry.devicePixelContentBoxSize?.[0].inlineSize || + entry.contentBoxSize[0].inlineSize * window.devicePixelRatio; + const height = + entry.devicePixelContentBoxSize?.[0].blockSize || + entry.contentBoxSize[0].blockSize * window.devicePixelRatio; + canvas.width = Math.max(1, Math.min(width, root.device.limits.maxTextureDimension2D)); + canvas.height = Math.max(1, Math.min(height, root.device.limits.maxTextureDimension2D)); +}); +try { + observer.observe(canvas, { box: 'device-pixel-content-box' }); +} catch { + observer.observe(canvas, { box: 'content-box' }); +} + +requestAnimationFrame(render); diff --git a/packages/typegpu-cli/templates/template-vite-complex/src/style.css b/packages/typegpu-cli/templates/template-vite-complex/src/style.css new file mode 100644 index 0000000000..5deb51e380 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/src/style.css @@ -0,0 +1,315 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +.slider { + appearance: none; + width: min(260px, 70vw); + height: 24px; + background: transparent; + border: none; + outline: none; + cursor: pointer; + touch-action: pan-y; + margin-bottom: 24px; + + &::-webkit-slider-runnable-track { + height: 6px; + border-radius: 999px; + background: linear-gradient(90deg, #4e65f6, var(--accent)); + box-shadow: 0 0 12px rgba(170, 59, 255, 0.18); + } + + &::-webkit-slider-thumb { + appearance: none; + width: 18px; + height: 18px; + margin-top: -6px; + border: 2px solid var(--bg); + border-radius: 50%; + background: var(--accent); + cursor: pointer; + box-shadow: 0 2px 8px rgba(8, 6, 13, 0.22); + transition: transform 0.2s; + } + + &::-moz-range-track { + height: 6px; + background: linear-gradient(90deg, #4e65f6, var(--accent)); + border-radius: 999px; + border: none; + box-shadow: 0 0 12px rgba(170, 59, 255, 0.18); + } + + &::-moz-range-progress { + height: 6px; + background: linear-gradient(90deg, #4e65f6, var(--accent)); + border-radius: 999px; + } + + &::-moz-range-thumb { + width: 14px; + height: 14px; + border: 2px solid var(--bg); + border-radius: 50%; + background: var(--accent); + cursor: pointer; + box-shadow: 0 2px 8px rgba(8, 6, 13, 0.22); + transition: transform 0.2s; + } + + &:active::-webkit-slider-thumb { + transform: scale(0.92); + } + + &:active::-moz-range-thumb { + transform: scale(0.92); + } + + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 4px; + } +} + +#typegpu picture { + display: flex; + align-items: center; + height: 28px; + margin: 0 0 8px; + + @media (max-width: 1024px) { + height: 24px; + justify-content: center; + } +} + +.typegpu-logo { + display: block; + height: 48px; + width: auto; + + @media (max-width: 1024px) { + height: 32px; + margin: 0; + } +} + +.title-with-icon { + display: flex; + align-items: center; + gap: 8px; + + @media (max-width: 1024px) { + justify-content: center; + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + padding-top: 40px; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#canvas { + width: min(55vw, 55svh); + height: min(55vw, 55svh); + max-width: 100%; +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + min-width: 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#typegpu { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + flex-wrap: wrap; + gap: 8px; + margin: 32px 0 0; + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/tsconfig.json b/packages/typegpu-cli/templates/template-vite-complex/tsconfig.json new file mode 100644 index 0000000000..f1d813d9e3 --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "types": ["@webgpu/types", "vite/client"], + "skipLibCheck": true, + + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + "strict": true, + "noImplicitReturns": true, + "exactOptionalPropertyTypes": true, + "erasableSyntaxOnly": true + }, + "include": ["src"] +} diff --git a/packages/typegpu-cli/templates/template-vite-complex/vite.config.ts b/packages/typegpu-cli/templates/template-vite-complex/vite.config.ts new file mode 100644 index 0000000000..7b6199856c --- /dev/null +++ b/packages/typegpu-cli/templates/template-vite-complex/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +import typegpu from 'unplugin-typegpu/vite'; + +export default defineConfig({ + plugins: [typegpu({})], +}); From f13c66141b9ece5d9b29122d9dbbb06dc4e660b2 Mon Sep 17 00:00:00 2001 From: Szymon Szulc Date: Tue, 26 May 2026 15:18:46 +0200 Subject: [PATCH 4/5] package name fix --- .../typegpu-cli/templates/template-vite-complex/_package.json | 2 +- packages/typegpu-cli/templates/template-vite-complex/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/typegpu-cli/templates/template-vite-complex/_package.json b/packages/typegpu-cli/templates/template-vite-complex/_package.json index e40bfa0590..d8385740d0 100644 --- a/packages/typegpu-cli/templates/template-vite-complex/_package.json +++ b/packages/typegpu-cli/templates/template-vite-complex/_package.json @@ -1,5 +1,5 @@ { - "name": "typegpu-vanilla-vite-simple-project", + "name": "typegpu-vanilla-vite-complex-project", "version": "0.0.0", "private": true, "type": "module", diff --git a/packages/typegpu-cli/templates/template-vite-complex/index.html b/packages/typegpu-cli/templates/template-vite-complex/index.html index 3edee33107..2ae93ce68e 100644 --- a/packages/typegpu-cli/templates/template-vite-complex/index.html +++ b/packages/typegpu-cli/templates/template-vite-complex/index.html @@ -5,7 +5,7 @@ - typegpu-vanilla-vite-project + typegpu-vanilla-vite-complex-project
From 13647a93d4b8236be83a2343b31020fc156b027e Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Thu, 28 May 2026 07:17:04 +0200 Subject: [PATCH 5/5] Cleanup --- .../template-vite-complex/src/main.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/typegpu-cli/templates/template-vite-complex/src/main.ts b/packages/typegpu-cli/templates/template-vite-complex/src/main.ts index f89fa831eb..1a92565541 100644 --- a/packages/typegpu-cli/templates/template-vite-complex/src/main.ts +++ b/packages/typegpu-cli/templates/template-vite-complex/src/main.ts @@ -1,31 +1,33 @@ +import tgpu, { common, d, std } from 'typegpu'; +import { hexToOklab, oklabToRgb } from '@typegpu/color'; +import { perlin2d } from '@typegpu/noise'; + +// Wiring up the speed slider const slider = document.querySelector('#slider') as HTMLInputElement; let speed = Number(slider.value); slider.addEventListener('input', () => { speed = Number(slider.value); }); -import tgpu, { common, d, std } from 'typegpu'; -import { hexToOklab, oklabToRgb } from '@typegpu/color'; -import { perlin2d } from '@typegpu/noise'; - +// Initializing TypeGPU const root = await tgpu.init(); + const time = root.createUniform(d.f32); const canvas = document.querySelector('#canvas') as HTMLCanvasElement; - const context = root.configureContext({ canvas }); -const noise = (v: d.v2f) => { +function noise(v: d.v2f) { 'use gpu'; return perlin2d.sample(v); -}; +} const octavesAccessor = tgpu.accessor(d.u32); const standardizeNoiseAccessor = tgpu.accessor(d.bool); const getMaxValue = tgpu.comptime((octaves: number) => 1 - 2 ** -octaves); const rotation = d.mat2x2f(0.8, 0.6, -0.6, 0.8); -const fbm = tgpu.fn((v: d.v2f) => { +function fbm(v: d.v2f) { 'use gpu'; let u = d.vec2f(v); let f = d.f32(); @@ -54,17 +56,17 @@ const fbm = tgpu.fn((v: d.v2f) => { } return f / getMaxValue(octavesAccessor.$); -}); +} -const fbm4 = fbm.with(octavesAccessor, 4).with(standardizeNoiseAccessor, false); -const fbm6 = fbm.with(octavesAccessor, 6).with(standardizeNoiseAccessor, true); +const fbm4 = tgpu.fn(fbm).with(octavesAccessor, 4).with(standardizeNoiseAccessor, false); +const fbm6 = tgpu.fn(fbm).with(octavesAccessor, 6).with(standardizeNoiseAccessor, true); -const domainWarp = (v: d.v2f) => { +function domainWarp(v: d.v2f) { 'use gpu'; return fbm6(v + fbm4(v + fbm4(v))); -}; +} -const palette = (t: number) => { +function palette(t: number) { 'use gpu'; const purple = hexToOklab('#c04bf2'); const blue = hexToOklab('#4e65f6'); @@ -77,11 +79,12 @@ const palette = (t: number) => { const mixed = std.mix(std.mix(dark, blue, factor1), purple, factor2); return oklabToRgb(mixed); -}; +} const VIRTUAL_GRID_SIZE = 4; const EPS = 0.04; const DIFF_SCALE = 0.4; + const pipeline = root.createRenderPipeline({ vertex: common.fullScreenTriangle, fragment: ({ uv }) => { @@ -115,10 +118,9 @@ function render(timestamp: number) { if (lastTimestamp !== null) { elapsed += ((timestamp - lastTimestamp) / 1000.0) * speed; } - lastTimestamp = timestamp; - time.write(elapsed); + time.write(elapsed); pipeline.withColorAttachment({ view: context }).draw(3); requestAnimationFrame(render);