website update

This commit is contained in:
Orion Reed 2024-12-22 23:30:24 -05:00
parent e69f60b59f
commit 8e6e4380f4
23 changed files with 247 additions and 55 deletions

View File

@ -97,6 +97,7 @@ uniform int materialType;
uniform float brushRadius; uniform float brushRadius;
uniform sampler2D tex; uniform sampler2D tex;
uniform sampler2D u_collisionTex; uniform sampler2D u_collisionTex;
uniform float initialSand;
in vec2 outUv; in vec2 outUv;
@ -196,7 +197,7 @@ void main() {
if (frame == 0) { if (frame == 0) {
float r = hash12(gl_FragCoord.xy); float r = hash12(gl_FragCoord.xy);
float id = AIR; float id = AIR;
if (r < 0.15) if (r < initialSand)
{ {
id = SAND; id = SAND;
} }

View File

@ -25,6 +25,12 @@ export class FolkSand extends FolkBaseSet {
`, `,
]; ];
static properties = {
initialSand: { type: Number, attribute: 'initial-sand' },
};
initialSand = 0.15;
#canvas = document.createElement('canvas'); #canvas = document.createElement('canvas');
#gl!: WebGL2RenderingContext; #gl!: WebGL2RenderingContext;
@ -152,7 +158,17 @@ export class FolkSand extends FolkBaseSet {
// Create collision texture // Create collision texture
this.#collisionTex = gl.createTexture()!; this.#collisionTex = gl.createTexture()!;
gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex); gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, this.#bufferWidth, this.#bufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA8,
this.#bufferWidth,
this.#bufferHeight,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null,
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
@ -425,6 +441,9 @@ export class FolkSand extends FolkBaseSet {
gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex); gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex);
} }
const initialSandLoc = gl.getUniformLocation(this.#program, 'initialSand');
gl.uniform1f(initialSandLoc, this.initialSand);
let mx = (this.#pointer.x / gl.canvas.width) * this.#bufferWidth; let mx = (this.#pointer.x / gl.canvas.width) * this.#bufferWidth;
let my = (1.0 - this.#pointer.y / gl.canvas.height) * this.#bufferHeight; let my = (1.0 - this.#pointer.y / gl.canvas.height) * this.#bufferHeight;
let mpx = (this.#pointer.prevX / gl.canvas.width) * this.#bufferWidth; let mpx = (this.#pointer.prevX / gl.canvas.width) * this.#bufferWidth;
@ -543,7 +562,17 @@ export class FolkSand extends FolkBaseSet {
// Update collision texture size // Update collision texture size
gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex); gl.bindTexture(gl.TEXTURE_2D, this.#collisionTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, this.#bufferWidth, this.#bufferHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA8,
this.#bufferWidth,
this.#bufferHeight,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
null,
);
// Re-render collision data after resize // Re-render collision data after resize
this.#handleShapeTransform(); this.#handleShapeTransform();
@ -587,13 +616,7 @@ export class FolkSand extends FolkBaseSet {
} }
} }
#createProgramFromStrings({ #createProgramFromStrings({ vertex, fragment }: { vertex: string; fragment: string }): WebGLProgram | undefined {
vertex,
fragment,
}: {
vertex: string;
fragment: string;
}): WebGLProgram | undefined {
const vertexShader = WebGLUtils.createShader(this.#gl, this.#gl.VERTEX_SHADER, vertex); const vertexShader = WebGLUtils.createShader(this.#gl, this.#gl.VERTEX_SHADER, vertex);
const fragmentShader = WebGLUtils.createShader(this.#gl, this.#gl.FRAGMENT_SHADER, fragment); const fragmentShader = WebGLUtils.createShader(this.#gl, this.#gl.FRAGMENT_SHADER, fragment);

View File

@ -6,7 +6,27 @@ import mkcert from 'vite-plugin-mkcert';
const canvasWebsiteDir = resolve(__dirname, './website/canvas'); const canvasWebsiteDir = resolve(__dirname, './website/canvas');
function getCanvasFiles() { function getCanvasFiles() {
return readdirSync(canvasWebsiteDir).filter((file) => file.endsWith('.html')); const files: { path: string; name: string }[] = [];
// Helper function to read directory recursively
const readDir = (dir: string, base = '') => {
readdirSync(dir, { withFileTypes: true }).forEach((dirent) => {
// Skip directories that start with underscore
if (dirent.name.startsWith('_')) return;
if (dirent.isDirectory()) {
readDir(resolve(dir, dirent.name), `${base}${dirent.name}/`);
} else if (dirent.name.endsWith('.html')) {
files.push({
path: `${base}${dirent.name}`,
name: dirent.name,
});
}
});
};
readDir(canvasWebsiteDir);
return files;
} }
const linkGenerator = (): Plugin => { const linkGenerator = (): Plugin => {
@ -15,46 +35,41 @@ const linkGenerator = (): Plugin => {
transformIndexHtml(html: string, ctx: IndexHtmlTransformContext) { transformIndexHtml(html: string, ctx: IndexHtmlTransformContext) {
if (!ctx.filename.endsWith('canvas/index.html')) return; if (!ctx.filename.endsWith('canvas/index.html')) return;
const files = getCanvasFiles(); const files = getCanvasFiles();
// First, handle ungrouped files
const ungroupedFiles = files.filter(
(file) => !file.includes('index') && !file.startsWith('_') && !file.match(/^\[([^\]]+)\]/),
);
// Then handle grouped files // Handle ungrouped files (in root canvas directory)
const ungroupedFiles = files.filter((file) => !file.path.includes('/') && !file.name.includes('index'));
// Handle grouped files (in subdirectories)
const groups = files const groups = files
.filter((file) => !file.includes('index') && file.match(/^\[([^\]]+)\]/)) .filter((file) => file.path.includes('/'))
.reduce( .reduce(
(acc, file) => { (acc, file) => {
const match = file.match(/^\[([^\]]+)\](.+)\.html$/); const group = file.path.split('/')[0];
const group = match![1];
if (!acc[group]) acc[group] = []; if (!acc[group]) acc[group] = [];
acc[group].push(file); acc[group].push(file);
return acc; return acc;
}, },
{} as Record<string, string[]>, {} as Record<string, typeof files>,
); );
// Generate ungrouped HTML first // Generate ungrouped HTML
const ungroupedHtml = ungroupedFiles const ungroupedHtml = ungroupedFiles
.sort() .sort((a, b) => a.name.localeCompare(b.name))
.map((file) => { .map(({ path, name }) => {
const title = file.replace('.html', '').replaceAll('-', ' '); const title = name.replace('.html', '').replaceAll('-', ' ');
return `<li><a href="${file}">${title}</a></li>`; return `<li><a href="${path}">${title}</a></li>`;
}) })
.join('\n'); .join('\n');
// Then generate grouped HTML // Generate grouped HTML
const groupedHtml = Object.entries(groups) const groupedHtml = Object.entries(groups)
.sort(([a], [b]) => a.localeCompare(b)) .sort(([a], [b]) => a.localeCompare(b))
.map(([group, groupFiles]) => { .map(([group, groupFiles]) => {
const groupHtml = groupFiles const groupHtml = groupFiles
.sort() .sort((a, b) => a.name.localeCompare(b.name))
.map((file) => { .map(({ path, name }) => {
const title = file const title = name.replace('.html', '').replaceAll('-', ' ');
.replace(/^\[[^\]]+\]/, '') return `<li><a href="${path}">${title}</a></li>`;
.replace('.html', '')
.replaceAll('-', ' ');
return `<li><a href="${file}">${title}</a></li>`;
}) })
.join('\n'); .join('\n');
@ -84,7 +99,7 @@ export default defineConfig({
index: resolve(__dirname, './website/index.html'), index: resolve(__dirname, './website/index.html'),
...getCanvasFiles().reduce( ...getCanvasFiles().reduce(
(acc, file) => { (acc, file) => {
acc[`canvas/${file.replace('.html', '')}`] = resolve(canvasWebsiteDir, file); acc[`canvas/${file.name.replace('.html', '')}`] = resolve(canvasWebsiteDir, file.name);
return acc; return acc;
}, },
{} as Record<string, string>, {} as Record<string, string>,

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -6,7 +6,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link <link
href="https://fonts.googleapis.com/css2?family=Courier+Prime:ital,wght@0,400;0,700;1,400;1,700&family=Pixelify+Sans&display=swap" href="https://fonts.googleapis.com/css2?family=Recursive:slnt,wght,CASL,CRSV,MONO@-15..0,300..1000,0..1,0..1,0..1&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<title>Folk Canvas</title> <title>Folk Canvas</title>
@ -16,27 +16,50 @@
background: #f5f5f5; background: #f5f5f5;
} }
body {
margin: 0;
min-height: 100%;
}
folk-sand {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
main { main {
font-family: 'Courier Prime', Courier, monospace; position: relative;
font-family: 'Recursive', Courier, monospace;
font-variation-settings:
'slnt' 0,
'wght' 400,
'CASL' 1,
'CRSV' 0,
'MONO' 0;
max-width: 800px; max-width: 800px;
margin: 20px auto; margin: 20px auto;
padding: 20px; padding: 20px;
border-radius: 2px; border-radius: 2px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
background: #fff; background: rgba(255, 255, 255, 0.9);
} }
h1 { h1 {
color: #2c3e50;
margin: 0; margin: 0;
margin-bottom: 1em; margin-bottom: 1em;
text-align: center; text-align: center;
font-variation-settings:
'CASL' 0,
'MONO' 1;
} }
h2 { h2 {
color: #2c3e50;
font-size: 1.2em; font-size: 1.2em;
font-weight: 800;
margin: 1em 0 0; margin: 1em 0 0;
font-variation-settings: 'wght' 800;
} }
ul { ul {
@ -46,14 +69,24 @@
margin: 0; margin: 0;
} }
li {
display: grid;
grid-template-columns: 2em 1fr;
padding-left: 0;
text-indent: 0;
}
li::before { li::before {
content: '>'; content: '➔';
color: #666; justify-self: center;
margin-right: 1em; }
#questions li::before {
content: attr(data-symbol);
} }
a { a {
color: #34495e; color: #22282f;
text-decoration: none; text-decoration: none;
} }
@ -63,12 +96,8 @@
#questions { #questions {
font-size: 0.9rem; font-size: 0.9rem;
font-style: italic; font-variation-settings: 'slnt' 1;
margin-bottom: 2rem; margin-bottom: 2rem;
li::before {
content: '-';
}
} }
#disclaimer { #disclaimer {
@ -76,13 +105,134 @@
font-style: italic; font-style: italic;
} }
</style> </style>
<script type="module">
import '@labs/standalone/folk-sand.ts';
const alchemicalSymbols = [
'🜁',
'🜂',
'🜃',
'🜄',
'🜅',
'🜆',
'🜇',
'🜈',
'🜉',
'🜊',
'🜋',
'🜌',
'🜍',
'🜎',
'🜏',
'🜐',
'🜑',
'🜒',
'🜓',
'🜔',
'🜕',
'🜖',
'🜗',
'🜘',
'🜙',
'🜚',
'🜛',
'🜜',
'🜝',
'🜞',
'🜟',
'🜠',
'🜡',
'🜢',
'🜣',
'🜤',
'🜥',
'🜦',
'🜧',
'🜨',
'🜩',
'🜪',
'🜫',
'🜬',
'🜭',
'🜮',
'🜯',
'🜰',
'🜱',
'🜲',
'🜳',
'🜴',
'🜵',
'🜶',
'🜸',
'🜹',
'🜺',
'🜻',
'🜼',
'🜾',
'🜿',
'🝀',
'🝁',
'🝂',
'🝃',
'🝄',
'🝅',
'🝆',
'🝈',
'🝉',
'🝊',
'🝋',
'🝌',
'🝍',
'🝎',
'🝏',
'🝐',
'🝑',
'🝒',
'🝓',
'🝔',
'🝕',
'🝖',
'🝗',
'🝘',
'🝙',
'🝚',
'🝛',
'🝜',
'🝝',
'🝞',
'🝟',
'🝠',
'🝡',
'🝢',
'🝣',
'🝤',
'🝥',
'🝦',
'🝧',
'🝩',
'🝪',
'🝬',
'🝭',
'🝮',
'🝯',
'🝰',
'🝲',
'🝳',
];
document.addEventListener('DOMContentLoaded', () => {
const questionItems = document.querySelectorAll('#questions li');
questionItems.forEach((li) => {
const randomSymbol = alchemicalSymbols[Math.floor(Math.random() * alchemicalSymbols.length)];
li.setAttribute('data-symbol', randomSymbol);
});
});
</script>
</head> </head>
<body> <body>
<folk-sand initial-sand="0"></folk-sand>
<main> <main>
<h1> <h1> Folk Canvas [<a href="https://github.com/folk-canvas/folk-canvas">Github</a>] </h1>
Folk Canvas
<a href="https://github.com/folk-canvas/folk-canvas">(Github)</a>
</h1>
<ul id="questions"> <ul id="questions">
<li>How do we make the web feel more alive and less rigid and closed off?</li> <li>How do we make the web feel more alive and less rigid and closed off?</li>
<li>How can we more easily compose web pages and their data together?</li> <li>How can we more easily compose web pages and their data together?</li>
@ -99,8 +249,11 @@
> >
</ul> </ul>
<h2>Demos</h2> <h2>Experiments from the lab</h2>
<p id="disclaimer">(Make sure to checkout the dev tools, all of the you see are just authored in HTML!)</p> <p id="disclaimer">
These experiments (like this page) are very raw, intended to poke at new primitives and ideas. Make sure to
checkout the dev tools, all of the you see are just authored in HTML!
</p>
<ul id="links"> <ul id="links">
{{ LINKS }} {{ LINKS }}