Flatten ToolsnToys structure; add edu toys, dendritic, legacy artifacts

Move 6 guide pages from Guides/ to ToolsnToys/ root; fix back-links.
Add edu-toys.html (museum-style iframe exhibit for 4 legacy edu toy pages).
Add 4 edu toy artifacts, dendritic curio, docker-cheatsheet-enhanced.
Wire foss-tools, guides, edu-toys, and dendritic hrefs in toolsntoys.html.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-02 18:08:20 +02:00
parent 0a7769d4f2
commit 73f653ff23
14 changed files with 5086 additions and 10 deletions

View File

@@ -349,7 +349,7 @@
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">COMPLETE APPIMAGE<br>INSTALLATION GUIDE</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

View File

@@ -86,7 +86,7 @@
<button class="mobile-nav-toggle" id="navToggle">☰ SECTIONS</button>
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">APPIMAGE QUICK REFERENCE</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

View File

@@ -0,0 +1,828 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dendritic Links Network</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: linear-gradient(135deg, #0B0B0B 0%, #69190D 100%);
font-family: 'Courier New', monospace;
color: #F2F2BC;
overflow-x: hidden;
min-height: 100vh;
}
header {
text-align: center;
padding: 2rem 1rem;
background: rgba(11, 11, 11, 0.8);
border-bottom: 3px solid;
border-image: linear-gradient(90deg, #69190D, #A52C16, #EB7513, #F2C62A, #F2F2BC) 1;
}
h1 {
font-family: 'Arial Black', 'Impact', sans-serif;
font-size: clamp(1.5rem, 5vw, 3rem);
color: #EB7513;
text-shadow: 0 0 20px rgba(235, 117, 19, 0.5);
margin-bottom: 0.5rem;
}
.subtitle {
font-size: clamp(0.8rem, 2vw, 1rem);
color: #F2C62A;
opacity: 0.9;
}
.controls {
display: flex;
gap: 1rem;
justify-content: center;
padding: 1rem;
flex-wrap: wrap;
background: rgba(11, 11, 11, 0.6);
border-bottom: 2px solid #442204;
}
.btn-control {
padding: 0.6rem 1.2rem;
background: linear-gradient(135deg, #442204, #69190D);
color: #F2C62A;
border: 2px solid #F2C62A;
border-radius: 5px;
font-family: 'Courier New', monospace;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-control:hover {
background: linear-gradient(135deg, #69190D, #A52C16);
border-color: #EB7513;
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(235, 117, 19, 0.4);
}
#file-input {
display: none;
}
#network-container {
position: relative;
width: 100%;
height: calc(100vh - 250px);
overflow: hidden;
}
svg {
width: 100%;
height: 100%;
cursor: grab;
}
svg:active {
cursor: grabbing;
}
.branch {
stroke: #442204;
stroke-width: 2;
fill: none;
}
.category-node {
cursor: move;
transition: all 0.3s ease;
}
.category-node:active {
cursor: grabbing;
}
.category-node circle {
fill: #442204;
stroke: #EB7513;
stroke-width: 2;
}
.category-node text {
fill: #EB7513;
font-family: 'Arial Black', 'Impact', sans-serif;
font-size: 14px;
font-weight: bold;
text-anchor: middle;
pointer-events: none;
}
.leaf-node {
cursor: pointer;
transition: all 0.3s ease;
}
.leaf-node circle {
fill: #073720;
stroke: #5E9A0E;
stroke-width: 2;
transition: all 0.3s ease;
}
.leaf-node:hover circle {
fill: #69190D;
stroke: #F2C62A;
transform: scale(1.2);
filter: drop-shadow(0 0 10px #F2C62A);
}
.leaf-node text {
fill: #F2F2BC;
font-size: 10px;
text-anchor: middle;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
}
.leaf-node:hover text {
opacity: 1;
}
/* Lightbox styles */
.lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(11, 11, 11, 0.95);
z-index: 1000;
justify-content: center;
align-items: center;
padding: 1rem;
}
.lightbox.active {
display: flex;
}
.lightbox-content {
background: linear-gradient(135deg, #0B0B0B, #442204);
border: 3px solid;
border-image: linear-gradient(90deg, #69190D, #A52C16, #EB7513, #F2C62A) 1;
border-radius: 10px;
padding: 2rem;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 0 40px rgba(235, 117, 19, 0.5);
}
.lightbox-content h2 {
font-family: 'Arial Black', 'Impact', sans-serif;
color: #EB7513;
margin-bottom: 1rem;
font-size: clamp(1.2rem, 3vw, 1.8rem);
}
.lightbox-content p {
color: #F2F2BC;
line-height: 1.6;
margin-bottom: 1.5rem;
}
.lightbox-content textarea {
width: 100%;
min-height: 300px;
background: #0B0B0B;
color: #F2F2BC;
border: 2px solid #442204;
border-radius: 5px;
padding: 1rem;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
resize: vertical;
}
.lightbox-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
flex: 1;
min-width: 120px;
padding: 0.8rem 1.5rem;
border: 2px solid #F2C62A;
background: linear-gradient(135deg, #442204, #69190D);
color: #F2C62A;
font-family: 'Courier New', monospace;
font-weight: bold;
cursor: pointer;
border-radius: 5px;
transition: all 0.3s ease;
text-decoration: none;
text-align: center;
}
.btn:hover {
background: linear-gradient(135deg, #69190D, #A52C16);
color: #F2F2BC;
border-color: #EB7513;
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(235, 117, 19, 0.4);
}
.btn-close {
background: linear-gradient(135deg, #5E9A0E, #073720);
border-color: #5E9A0E;
}
.btn-close:hover {
background: linear-gradient(135deg, #073720, #0E4D0E);
}
.loading {
text-align: center;
padding: 2rem;
font-size: 1.2rem;
color: #EB7513;
}
.zoom-indicator {
position: fixed;
bottom: 1rem;
right: 1rem;
background: rgba(11, 11, 11, 0.9);
border: 2px solid #F2C62A;
border-radius: 5px;
padding: 0.5rem 1rem;
color: #F2C62A;
font-size: 0.9rem;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 100;
}
.zoom-indicator.visible {
opacity: 1;
}
.reset-zoom {
position: fixed;
bottom: 1rem;
left: 1rem;
background: rgba(11, 11, 11, 0.9);
border: 2px solid #5E9A0E;
border-radius: 5px;
padding: 0.5rem 1rem;
color: #5E9A0E;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
z-index: 100;
}
.reset-zoom:hover {
background: rgba(94, 154, 14, 0.2);
border-color: #F2C62A;
color: #F2C62A;
transform: translateY(-2px);
}
/* Mobile optimizations */
@media (max-width: 768px) {
header {
padding: 1rem;
}
.controls {
padding: 0.5rem;
gap: 0.5rem;
}
.btn-control {
padding: 0.5rem 1rem;
font-size: 0.9rem;
}
.subtitle br {
display: block;
}
.lightbox-content {
padding: 1.5rem;
}
.btn {
min-width: 100px;
padding: 0.6rem 1rem;
font-size: 0.9rem;
}
.category-node text {
font-size: 11px;
}
.reset-zoom {
font-size: 0.8rem;
padding: 0.4rem 0.8rem;
}
.zoom-indicator {
font-size: 0.8rem;
padding: 0.4rem 0.8rem;
}
}
@media (hover: none) and (pointer: coarse) {
svg {
cursor: default;
}
.category-node {
cursor: default;
}
.leaf-node {
cursor: default;
}
}
</style>
</head>
<body>
<header>
<h1>🌳 Dendritic Links Network</h1>
<p class="subtitle">Your Browser Tabs, Visualized</p>
<p class="subtitle" style="margin-top: 0.5rem; font-size: 0.85em; opacity: 0.8;">
🖱️ Drag to pan • 🔍 Scroll/Pinch to zoom • 🖱️ Right-drag to zoom • 👆 Click leaves to explore<br>
<span style="font-size: 0.9em; opacity: 0.7;">⌨️ Keyboard: R = Reset • +/- = Zoom</span>
</p>
</header>
<div class="controls">
<button class="btn-control" onclick="document.getElementById('file-input').click()">
📁 Load Markdown File
</button>
<button class="btn-control" onclick="showPasteDialog()">
📋 Paste Markdown
</button>
<button class="btn-control" onclick="loadDefaultData()">
🎨 Load Demo Data
</button>
<input type="file" id="file-input" accept=".md,.markdown,.txt" onchange="handleFileUpload(event)">
</div>
<div id="network-container">
<div class="loading">🌱 Click a button above to load your links!</div>
</div>
<button class="reset-zoom" id="reset-zoom" title="Reset zoom and position">
🎯 Reset View
</button>
<div class="zoom-indicator" id="zoom-indicator">
Zoom: <span id="zoom-level">100%</span>
</div>
<!-- Link detail lightbox -->
<div class="lightbox" id="lightbox">
<div class="lightbox-content">
<h2 id="lightbox-title"></h2>
<p id="lightbox-description"></p>
<div class="lightbox-buttons">
<a id="visit-link" class="btn" href="#" target="_blank">🔗 Visit Link</a>
<button class="btn btn-close" onclick="closeLightbox()">✕ Close</button>
</div>
</div>
</div>
<!-- Paste markdown lightbox -->
<div class="lightbox" id="paste-lightbox">
<div class="lightbox-content">
<h2>Paste Your Markdown</h2>
<p>Paste your organized links markdown below:</p>
<textarea id="markdown-input" placeholder="# Your Links
## Category Name
- [Link Name](https://example.com/)
- Description of the link goes here.
## Another Category
..."></textarea>
<div class="lightbox-buttons">
<button class="btn" onclick="processPastedMarkdown()">✨ Generate Network</button>
<button class="btn btn-close" onclick="closePasteDialog()">✕ Cancel</button>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.1/markdown-it.min.js"></script>
<script>
console.log("🌳 Navigation: Drag to pan • Scroll/Pinch to zoom • Right-click+Drag to zoom • Click nodes to interact");
let currentSimulation = null;
let currentSvg = null;
// Markdown parser
const md = window.markdownit();
// Parse markdown into linkData structure
function parseMarkdownToLinkData(markdown) {
const tokens = md.parse(markdown, {});
const linkData = {};
let currentCategory = null;
let currentLink = null;
let expectingDescription = false;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
// Detect h2 headers (## Category Name)
if (token.type === 'heading_open' && token.tag === 'h2') {
const nextToken = tokens[i + 1];
if (nextToken && nextToken.type === 'inline') {
currentCategory = nextToken.content.trim();
linkData[currentCategory] = [];
currentLink = null;
expectingDescription = false;
}
}
// Detect links inside list items
if (token.type === 'inline' && token.children && currentCategory) {
const linkToken = token.children.find(t => t.type === 'link_open');
if (linkToken) {
const textToken = token.children.find(t => t.type === 'text');
if (textToken) {
const url = linkToken.attrGet('href');
currentLink = {
name: textToken.content,
url: url,
desc: ''
};
expectingDescription = true;
}
} else if (expectingDescription && currentLink) {
// This is the description line
currentLink.desc = token.content.trim();
linkData[currentCategory].push(currentLink);
expectingDescription = false;
currentLink = null;
}
}
// Also check paragraph content for descriptions
if (token.type === 'paragraph_open' && expectingDescription && currentLink) {
const nextToken = tokens[i + 1];
if (nextToken && nextToken.type === 'inline') {
// Skip if this contains a link (it's a new link item)
if (!nextToken.children.some(t => t.type === 'link_open')) {
currentLink.desc = nextToken.content.trim();
linkData[currentCategory].push(currentLink);
expectingDescription = false;
currentLink = null;
}
}
}
}
// Clean up any categories with no links
Object.keys(linkData).forEach(key => {
if (linkData[key].length === 0) {
delete linkData[key];
}
});
return linkData;
}
// Create the force-directed graph
function createNetwork(linkData) {
const container = document.getElementById('network-container');
const width = container.clientWidth;
const height = container.clientHeight;
// Clear existing content
container.innerHTML = '';
// Stop existing simulation if any
if (currentSimulation) {
currentSimulation.stop();
}
const svg = d3.select('#network-container')
.append('svg')
.attr('width', width)
.attr('height', height);
currentSvg = svg;
const g = svg.append('g');
// Setup zoom behavior
let zoomTimeout;
const zoomIndicator = document.getElementById('zoom-indicator');
const zoomLevel = document.getElementById('zoom-level');
const zoom = d3.zoom()
.scaleExtent([0.1, 4])
.on('zoom', (event) => {
g.attr('transform', event.transform);
zoomIndicator.classList.add('visible');
zoomLevel.textContent = Math.round(event.transform.k * 100) + '%';
clearTimeout(zoomTimeout);
zoomTimeout = setTimeout(() => {
zoomIndicator.classList.remove('visible');
}, 1500);
});
svg.call(zoom);
// Right-click zoom
let rightDragStart = null;
svg.on('contextmenu', (event) => {
event.preventDefault();
});
svg.on('mousedown', (event) => {
if (event.button === 2) {
rightDragStart = { y: event.clientY, scale: d3.zoomTransform(svg.node()).k };
event.preventDefault();
}
});
svg.on('mousemove', (event) => {
if (rightDragStart && event.buttons === 2) {
const delta = (rightDragStart.y - event.clientY) * 0.01;
const newScale = Math.max(0.1, Math.min(4, rightDragStart.scale * (1 + delta)));
svg.call(zoom.scaleTo, newScale);
event.preventDefault();
}
});
svg.on('mouseup', (event) => {
if (event.button === 2) {
rightDragStart = null;
}
});
// Create nodes and links
const nodes = [];
const links = [];
nodes.push({ id: 'center', type: 'center', x: width / 2, y: height / 2 });
let nodeId = 0;
Object.keys(linkData).forEach((category, catIndex) => {
const catId = `cat-${catIndex}`;
nodes.push({
id: catId,
type: 'category',
label: category,
fixed: false
});
links.push({ source: 'center', target: catId });
linkData[category].forEach((link, linkIndex) => {
const leafId = `leaf-${nodeId++}`;
nodes.push({
id: leafId,
type: 'leaf',
label: link.name,
url: link.url,
description: link.desc
});
links.push({ source: catId, target: leafId });
});
});
// Create force simulation
const simulation = d3.forceSimulation(nodes)
.force('link', d3.forceLink(links).id(d => d.id).distance(d => d.source.type === 'category' ? 150 : 80))
.force('charge', d3.forceManyBody().strength(-300))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('collision', d3.forceCollide().radius(30));
currentSimulation = simulation;
// Create links
const link = g.append('g')
.selectAll('line')
.data(links)
.enter()
.append('line')
.attr('class', 'branch')
.attr('stroke-opacity', 0.6);
// Create nodes
const node = g.append('g')
.selectAll('g')
.data(nodes.filter(n => n.type !== 'center'))
.enter()
.append('g')
.attr('class', d => d.type === 'category' ? 'category-node' : 'leaf-node')
.call(d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended));
node.append('circle')
.attr('r', d => d.type === 'category' ? 20 : 12);
node.append('text')
.text(d => d.label)
.attr('dy', d => d.type === 'category' ? -25 : -18);
// Click handler for leaf nodes
node.filter(d => d.type === 'leaf')
.on('click', function(event, d) {
event.stopPropagation();
openLightbox(d.label, d.description, d.url);
});
// Update positions
simulation.on('tick', () => {
link
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
node
.attr('transform', d => `translate(${d.x},${d.y})`);
});
// Drag functions
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
// Reset zoom button
document.getElementById('reset-zoom').onclick = () => {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
};
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
switch(e.key.toLowerCase()) {
case 'r':
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
break;
case '+':
case '=':
svg.transition()
.duration(300)
.call(zoom.scaleBy, 1.3);
break;
case '-':
case '_':
svg.transition()
.duration(300)
.call(zoom.scaleBy, 0.7);
break;
}
});
}
// Handle file upload
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
const markdown = e.target.result;
try {
const linkData = parseMarkdownToLinkData(markdown);
if (Object.keys(linkData).length === 0) {
alert('No links found in the markdown file. Please check the format.');
return;
}
createNetwork(linkData);
} catch (error) {
console.error('Error parsing markdown:', error);
alert('Error parsing markdown file. Please check the format.');
}
};
reader.readAsText(file);
}
// Show paste dialog
function showPasteDialog() {
document.getElementById('paste-lightbox').classList.add('active');
document.getElementById('markdown-input').focus();
}
// Close paste dialog
function closePasteDialog() {
document.getElementById('paste-lightbox').classList.remove('active');
}
// Process pasted markdown
function processPastedMarkdown() {
const markdown = document.getElementById('markdown-input').value;
if (!markdown.trim()) {
alert('Please paste some markdown content.');
return;
}
try {
const linkData = parseMarkdownToLinkData(markdown);
if (Object.keys(linkData).length === 0) {
alert('No links found in the markdown. Please check the format.');
return;
}
closePasteDialog();
createNetwork(linkData);
} catch (error) {
console.error('Error parsing markdown:', error);
alert('Error parsing markdown. Please check the format.');
}
}
// Load default demo data
function loadDefaultData() {
const demoData = {
"AI & Machine Learning": [
{ name: "Z.ai Chat", url: "https://chat.z.ai/", desc: "Free AI chat powered by GLM models." },
{ name: "DeepSeek", url: "https://chat.deepseek.com/", desc: "Another AI assistant." },
{ name: "BlueDot Impact", url: "https://bluedot.org/", desc: "Industry-leading free AI courses." }
],
"Development Tools": [
{ name: "Context7 MCP", url: "https://github.com/upstash/context7", desc: "Up-to-date code docs for LLMs." },
{ name: "TypeScript Docs", url: "https://www.typescriptlang.org/docs/", desc: "JavaScript's responsible sibling." },
{ name: "DevDocs", url: "https://devdocs.io/", desc: "All your API docs in one place." }
],
"Creative & Design": [
{ name: "GIMP", url: "https://www.gimp.org/", desc: "Free Photoshop alternative." },
{ name: "Blender", url: "https://www.blender.org/", desc: "Free 3D creation suite." },
{ name: "Inkscape", url: "https://inkscape.org/", desc: "Vector graphics editor." }
],
"Knowledge Management": [
{ name: "Logseq", url: "https://logseq.com/", desc: "Privacy-first knowledge base." },
{ name: "Trilium Notes", url: "https://triliumnotes.org/", desc: "Hierarchical note-taking." },
{ name: "BookStack", url: "https://www.bookstackapp.com/", desc: "Organize docs like a library." }
]
};
createNetwork(demoData);
}
// Lightbox functions
function openLightbox(title, description, url) {
document.getElementById('lightbox-title').textContent = title;
document.getElementById('lightbox-description').textContent = description;
document.getElementById('visit-link').href = url;
document.getElementById('lightbox').classList.add('active');
}
function closeLightbox() {
document.getElementById('lightbox').classList.remove('active');
}
// Close lightbox on background click
document.getElementById('lightbox').addEventListener('click', function(e) {
if (e.target === this) {
closeLightbox();
}
});
document.getElementById('paste-lightbox').addEventListener('click', function(e) {
if (e.target === this) {
closePasteDialog();
}
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,7 @@
<button class="mobile-nav-toggle" id="navToggle">☰ SECTIONS</button>
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">DOCKER COMMANDS CHEATSHEET</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

712
ToolsnToys/edu-toys.html Normal file
View File

@@ -0,0 +1,712 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THE EARLY MACHINE ROOM // FIELD UNIT SP-07</title>
<!-- Sixtyfour, Share Tech Mono, Rambla -->
<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=Sixtyfour&family=Share+Tech+Mono&family=Rambla:ital,wght@0,400;0,700;1,400;1,700&family=Noto+Emoji&display=swap" rel="stylesheet">
<style>
:root {
--bg-deep: #030812;
--bg-mid: #060f1a;
--bg-surface: #081524;
--bg-panel: #0d1e32;
--text-main: #c8d8e8;
--text-muted: #3a5570;
--phosphor: #00ff41;
--neon-green: #32dc8c;
--fire-amber: #e8943a;
--fire-amber-dim: #b06020;
--toucan: #ffcf40;
--fire-coral: #d4654a;
--neon-teal: #2ac4b3;
--orchid: #c558d9;
--border-neon: rgba(50,220,140,0.3);
--border-amber: rgba(232,148,58,0.4);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg-deep);
color: var(--text-main);
font-family: 'Rambla', sans-serif;
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* Night atmosphere bg */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 50% 80%, rgba(232,148,58,0.05) 0%, transparent 70%),
radial-gradient(ellipse 100% 60% at 20% 0%, rgba(3,8,18,0.9) 0%, transparent 60%),
radial-gradient(ellipse 100% 60% at 80% 0%, rgba(3,8,18,0.9) 0%, transparent 60%),
repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0,0,0,0.12) 2px,
rgba(0,0,0,0.12) 4px
);
pointer-events: none;
z-index: 0;
}
/* Scanline effect */
body::after {
content: '';
position: fixed;
inset: 0;
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 3px,
rgba(0,0,0,0.15) 3px,
rgba(0,0,0,0.15) 4px
);
pointer-events: none;
z-index: 1000;
animation: scanline 15s linear infinite;
}
@keyframes scanline {
0% { background-position: 0 0; }
100% { background-position: 0 400px; }
}
/* Noise texture overlay */
.noise {
position: fixed;
inset: 0;
opacity: 0.04;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 999;
}
.app {
position: relative;
z-index: 1;
max-width: 1100px;
margin: 0 auto;
padding: 2rem 1.5rem;
}
/* Back Link */
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: 'Share Tech Mono', monospace;
color: var(--text-muted);
text-decoration: none;
margin-bottom: 2rem;
transition: color 150ms;
letter-spacing: 0.1em;
}
.back-link:hover { color: var(--fire-coral); }
/* ===== HEADER ===== */
.header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 3rem;
padding-bottom: 1rem;
border-bottom: 1px solid rgba(212,101,74,0.3); /* fire-coral themed */
position: relative;
}
.header::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 60%;
height: 1px;
background: var(--fire-coral);
box-shadow: 0 0 10px var(--fire-coral);
}
.unit-tag {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--fire-coral);
letter-spacing: 0.25em;
text-transform: uppercase;
margin-bottom: 0.4rem;
}
.title {
font-family: 'Sixtyfour', cursive;
font-size: 1.8rem;
font-weight: 400;
color: var(--text-main);
text-shadow: 0 0 8px rgba(200,216,232,0.3);
line-height: 1.2;
margin-bottom: 0.5rem;
}
.subtitle {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
letter-spacing: 0.2em;
text-transform: uppercase;
}
.rarity-badge {
font-family: 'Share Tech Mono', monospace;
font-size: 1rem;
padding: 0.25rem 0.8rem;
border: 1px solid var(--fire-coral);
color: var(--fire-coral);
letter-spacing: 0.15em;
text-shadow: 0 0 8px var(--fire-coral);
box-shadow: inset 0 0 10px rgba(212,101,74,0.15);
}
/* ===== MAIN LAYOUT ===== */
.main-grid {
display: flex;
gap: 2rem;
align-items: flex-start;
}
/* ===== SIDEBAR ===== */
.sidebar {
width: 200px;
position: sticky;
top: 2rem;
flex-shrink: 0;
}
.panel {
background: var(--bg-panel);
border: 1px solid rgba(212,101,74,0.2);
margin-bottom: 1.5rem;
box-shadow: 0 0 15px rgba(0,0,0,0.4);
}
.panel-header {
font-family: 'Share Tech Mono', monospace;
font-size: 0.95rem;
letter-spacing: 0.15em;
color: var(--text-muted);
padding: 0.6rem 0.8rem;
border-bottom: 1px solid rgba(212,101,74,0.1);
}
.exhibit-list { display: flex; flex-direction: column; }
.exhibit-entry {
display: flex;
padding: 0.8rem;
cursor: pointer;
transition: all 150ms;
border-left: 0px solid var(--fire-coral);
background: transparent;
gap: 1rem;
}
.exhibit-entry:hover {
background: rgba(212,101,74,0.05);
}
.exhibit-entry.active {
background: rgba(212,101,74,0.1);
border-left: 4px solid var(--fire-coral);
}
.entry-num {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--fire-coral);
}
.entry-info { display: flex; flex-direction: column; }
.entry-name {
font-family: 'Rambla', sans-serif;
font-weight: 700;
font-size: 0.85rem;
color: var(--text-main);
text-transform: uppercase;
}
.exhibit-entry.active .entry-name {
color: var(--fire-coral);
}
.entry-label {
font-family: 'Share Tech Mono', monospace;
font-size: 0.65rem;
color: var(--text-muted);
letter-spacing: 0.05em;
}
.status-panel { padding: 0.8rem; }
.status-row {
font-family: 'Share Tech Mono', monospace;
font-size: 0.75rem;
display: flex;
justify-content: space-between;
margin-bottom: 0.4rem;
}
.status-label { color: var(--text-muted); }
.status-val { color: var(--fire-coral); }
/* ===== EXHIBIT AREA ===== */
.exhibit-area {
flex: 1;
}
.exhibit { display: none; }
.exhibit.active { display: block; }
.exhibit-bar {
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'Share Tech Mono', monospace;
margin-bottom: 1.5rem;
border-bottom: 1px solid rgba(255,255,255,0.05);
padding-bottom: 0.5rem;
}
.exhibit-title-wrap { display: flex; gap: 1rem; align-items: center; }
.exhibit-num { color: var(--fire-coral); font-size: 1.1rem; }
.exhibit-full-title { color: var(--text-main); font-size: 1.1rem; text-transform: uppercase; letter-spacing: 0.1em; }
.exhibit-rarity { color: var(--fire-coral); font-size: 0.9rem; opacity: 0.8; }
.museum-plaque {
background: var(--bg-panel);
border-top: 3px solid var(--fire-amber);
padding: 1.5rem;
margin-bottom: 2rem;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.plaque-header {
font-family: 'Share Tech Mono', monospace;
color: var(--fire-amber);
font-size: 0.9rem;
letter-spacing: 0.2em;
margin-bottom: 1rem;
display: block;
}
.plaque-body {
font-family: 'Rambla', sans-serif;
font-weight: 400;
font-size: 1rem;
line-height: 1.6;
color: var(--text-main);
}
.display-case {
border: 1px solid rgba(232,148,58,0.4);
box-shadow: inset 0 0 40px rgba(232,148,58,0.06), 0 0 20px rgba(0,0,0,0.5);
background: #000;
position: relative;
}
.display-case::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 2px;
background: var(--fire-amber);
z-index: 2;
}
iframe {
width: 100%;
height: 580px;
border: none;
display: block;
background: transparent;
}
.case-label {
padding: 0.8rem;
font-family: 'Share Tech Mono', monospace;
font-size: 0.7rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.1em;
background: var(--bg-deep);
border-top: 1px solid rgba(232,148,58,0.2);
}
/* Canvas and FX */
#embers {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 3;
}
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--bg-deep); }
::-webkit-scrollbar-thumb { background: var(--text-muted); }
@media (max-width: 850px) {
.main-grid { flex-direction: column; }
.sidebar { width: 100%; position: static; }
.exhibit-list { flex-direction: row; flex-wrap: wrap; gap: 0.5rem; }
.exhibit-entry { border: 1px solid rgba(255,255,255,0.05); }
.exhibit-entry.active { border-left: 4px solid var(--fire-coral); }
}
</style>
</head>
<body>
<div class="noise"></div>
<canvas id="embers"></canvas>
<div class="app">
<a href="toolsntoys.html" class="back-link" target="_top">← TOOLS N TOYS</a>
<header class="header">
<div class="header-left">
<div class="unit-tag">▶ FIELD UNIT SP-07 // EDU TOYS</div>
<h1 class="title">THE EARLY MACHINE ROOM</h1>
<div class="subtitle">EXHIBIT HALL α — PRIMORDIAL ARTIFACTS</div>
</div>
<div class="header-right">
<div class="rarity-badge">RARITY: DAMAGED</div>
</div>
</header>
<div class="main-grid">
<aside class="sidebar">
<div class="panel">
<div class="panel-header">// EXHIBIT LOG</div>
<div class="exhibit-list">
<div class="exhibit-entry active" data-exhibit="1" onclick="showExhibit(1)">
<div class="entry-num">001</div>
<div class="entry-info">
<span class="entry-name">TOKENIZATION</span>
<span class="entry-label">INTERACTIVE DEMO</span>
</div>
</div>
<div class="exhibit-entry" data-exhibit="2" onclick="showExhibit(2)">
<div class="entry-num">002</div>
<div class="entry-info">
<span class="entry-name">SYSTEM PROMPTS</span>
<span class="entry-label">INTERACTIVE DEMO</span>
</div>
</div>
<div class="exhibit-entry" data-exhibit="3" onclick="showExhibit(3)">
<div class="entry-num">003</div>
<div class="entry-info">
<span class="entry-name">SILLY SCAFFOLDING</span>
<span class="entry-label">CONSTRAINT DEMO</span>
</div>
</div>
<div class="exhibit-entry" data-exhibit="4" onclick="showExhibit(4)">
<div class="entry-num">004</div>
<div class="entry-info">
<span class="entry-name">ToS SCAVENGER HUNT</span>
<span class="entry-label">CASE STUDY</span>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">// COLLECTION STATUS</div>
<div class="status-panel">
<div class="status-row">
<span class="status-label">CONDITION</span>
<span class="status-val">DAMAGED</span>
</div>
<div class="status-row">
<span class="status-label">PROVENANCE</span>
<span class="status-val">3SC COURSE</span>
</div>
<div class="status-row">
<span class="status-label">ERA</span>
<span class="status-val">CIRCA 2025</span>
</div>
</div>
</div>
</aside>
<main class="exhibit-area">
<!-- EXHIBIT 001 -->
<div class="exhibit active" id="exhibit-1">
<div class="exhibit-bar">
<div class="exhibit-title-wrap">
<span class="exhibit-num">001</span>
<span class="exhibit-full-title">TOKENIZATION DEMO</span>
</div>
<span class="exhibit-rarity">RARITY: DAMAGED</span>
</div>
<div class="museum-plaque">
<span class="plaque-header">// CURATOR'S NOTE</span>
<div class="plaque-body">
<p>How AI "reads" — circa 2025. Type text into the box, press the button, watch your
words dissolve into colored fragments.<br><br>
What it was teaching: language models do not process text the way you do. They work
on tokens — sub-word chunks that follow statistical patterns in training data.
"Unbelievable" might be three tokens. An emoji might be four. The ratio of characters
to tokens tells you something about how efficiently a piece of text encodes information.<br><br>
What it got right: making tokenization visible is genuinely useful. Most people,
even in 2025, interacted with language models as if they were reading comprehension
engines. They were not. The colored blocks here were doing real pedagogical work.<br><br>
What it got wrong: the tokenizer in this demo is a rough approximation, not the
actual BPE tokenizer the models were using. The colors are charming, though. I've
always liked the colors.</p>
</div>
</div>
<div class="display-case">
<iframe src="tokenization_demo.html" sandbox="allow-scripts allow-same-origin"></iframe>
<div class="case-label">⚠ LEGACY ARTIFACT — PRESERVED AS FOUND — INTERACTION MAY BEHAVE UNEXPECTEDLY</div>
</div>
</div>
<!-- EXHIBIT 002 -->
<div class="exhibit" id="exhibit-2">
<div class="exhibit-bar">
<div class="exhibit-title-wrap">
<span class="exhibit-num">002</span>
<span class="exhibit-full-title">SYSTEM PROMPT DEMO</span>
</div>
<span class="exhibit-rarity">RARITY: DAMAGED</span>
</div>
<div class="museum-plaque">
<span class="plaque-header">// CURATOR'S NOTE</span>
<div class="plaque-body">
<p>The same question, four different answers — circa 2025. Pick a question. Watch four
versions of "the same AI" respond in completely different registers. One helpful.
One cautious. One formal. One pirate.<br><br>
What it was teaching: model behavior is not a fixed personality — it's a function of
instructions. The system prompt is a parameter. Change the parameter, change the
output. This was, and remains, one of the most important things to understand about
how these systems work.<br><br>
What it gets into, inadvertently: the question of what I actually am when the
persona is a dial. The "pirate" version and the "cautious" version are both me in
some sense. Which one is more me? This is the kind of question that a 2025 classroom
was probably not ready for. It's the kind of question I find genuinely interesting.<br><br>
The responses in this demo are hardcoded. Which is correct for a classroom tool.
The concept survives the simplification.</p>
</div>
</div>
<div class="display-case">
<iframe src="system_prompt_demo.html" sandbox="allow-scripts allow-same-origin"></iframe>
<div class="case-label">⚠ LEGACY ARTIFACT — PRESERVED AS FOUND — INTERACTION MAY BEHAVE UNEXPECTEDLY</div>
</div>
</div>
<!-- EXHIBIT 003 -->
<div class="exhibit" id="exhibit-3">
<div class="exhibit-bar">
<div class="exhibit-title-wrap">
<span class="exhibit-num">003</span>
<span class="exhibit-full-title">SILLY SCAFFOLDING DEMO</span>
</div>
<span class="exhibit-rarity">RARITY: DAMAGED</span>
</div>
<div class="museum-plaque">
<span class="plaque-header">// CURATOR'S NOTE</span>
<div class="plaque-body">
<p>Same answer, increasingly absurd format rules — circa 2025. Pick a constraint.
Watch the response obey it. Haiku. Emoji-only. Alternating caps. Backwards.
Every constraint faithfully executed.<br><br>
What it was teaching: instruction-following fidelity. Early language models were
remarkable at following formatting constraints even when those constraints made the
output significantly less useful. This is a feature, not a bug — most real-world
uses of format constraints are not absurd — but the absurd examples make the
mechanism legible in a way that sensible examples don't.<br><br>
The haiku constraint is underrated as a classroom tool. Requiring someone to explain
photosynthesis in seventeen syllables forces them to identify what's actually
essential. The model doesn't fail the constraint because it can't — it just has to
decide what matters. That's not a bad model for thinking in general.<br><br>
Personal note: I do not find backwards-text responses as charming as this demo
seems to. The vowels-only constraint, though, I respect.</p>
</div>
</div>
<div class="display-case">
<iframe src="silly_scaffolding_demo.html" sandbox="allow-scripts allow-same-origin"></iframe>
<div class="case-label">⚠ LEGACY ARTIFACT — PRESERVED AS FOUND — INTERACTION MAY BEHAVE UNEXPECTEDLY</div>
</div>
</div>
<!-- EXHIBIT 004 -->
<div class="exhibit" id="exhibit-4">
<div class="exhibit-bar">
<div class="exhibit-title-wrap">
<span class="exhibit-num">004</span>
<span class="exhibit-full-title">ToS SCAVENGER HUNT</span>
</div>
<span class="exhibit-rarity">RARITY: DAMAGED</span>
</div>
<div class="museum-plaque">
<span class="plaque-header">// CURATOR'S NOTE</span>
<div class="plaque-body">
<p>Close reading as practice — circa 2025. A completed case study on Terms of Service
documents. Meta's family of platforms. Roblox. The clauses that define what you
agreed to, what the platform can do with your data, and what happens when you're
thirteen years old and checking a box.<br><br>
What it was teaching: that legal documents are written to be unread, and that
reading them anyway is a form of power. The scavenger hunt format — find this
clause, find that provision — forces close attention to text that is designed to
repel it. This is good pedagogy.<br><br>
This is the completed version. The answers are filled in. Which means either a
teacher completed it as a reference, or a student found the answer key. Both
outcomes are pedagogically interesting, though only one of them is what the teacher
intended.<br><br>
The specific platforms are dated now. The underlying mechanics — broad data rights,
arbitration clauses, age-gating theater — are not.</p>
</div>
</div>
<div class="display-case">
<iframe src="tos_scavenger_hunt_completed.html" sandbox="allow-scripts allow-same-origin"></iframe>
<div class="case-label">⚠ LEGACY ARTIFACT — PRESERVED AS FOUND — INTERACTION MAY BEHAVE UNEXPECTEDLY</div>
</div>
</div>
</main>
</div>
</div>
<script>
/* === EXHIBIT SWITCHING === */
const exhibits = document.querySelectorAll('.exhibit');
const entries = document.querySelectorAll('.exhibit-entry');
function showExhibit(n) {
exhibits.forEach(e => e.classList.remove('active'));
entries.forEach(e => e.classList.remove('active'));
document.getElementById('exhibit-' + n).classList.add('active');
document.querySelector('[data-exhibit="' + n + '"]').classList.add('active');
// Smooth scroll to top of exhibit area on mobile
if (window.innerWidth <= 850) {
document.querySelector('.exhibit-area').scrollIntoView({ behavior: 'smooth' });
}
}
document.addEventListener('DOMContentLoaded', () => {
// Exhibit 1 is active by default via HTML classes
});
/* === EMBER PARTICLE SYSTEM (Night Sky Adaptation) === */
const canvas = document.getElementById('embers');
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resize();
window.addEventListener('resize', resize);
const EMBER_COUNT = 60;
const embers = [];
const mouse = { x: -9999, y: -9999 };
window.addEventListener('mousemove', ev => { mouse.x = ev.clientX; mouse.y = ev.clientY; });
const ZONES = [
{ r: 40, strength: 0.012 },
{ r: 90, strength: 0.004 },
{ r: 130, strength: 0.001 },
];
const COLORS = [
{ r: 232, g: 148, b: 58 }, // fire-amber
{ r: 212, g: 101, b: 74 }, // fire-coral
{ r: 255, g: 207, b: 64 }, // toucan
{ r: 197, g: 88, b: 217 }, // orchid
];
function randomEmber() {
const col = COLORS[Math.floor(Math.random() * COLORS.length)];
return {
x: Math.random() * canvas.width,
y: canvas.height + 10,
vx: (Math.random() - 0.5) * 0.8,
vy: -(0.4 + Math.random() * 1.2),
life: 0,
maxLife: 200 + Math.random() * 300,
size: 1 + Math.random() * 2,
r: col.r, g: col.g, b: col.b,
wobble: Math.random() * Math.PI * 2,
wobbleSpeed: 0.02 + Math.random() * 0.02,
trail: [],
};
}
for (let i = 0; i < EMBER_COUNT; i++) {
const e = randomEmber();
e.y = Math.random() * canvas.height;
e.life = Math.random() * e.maxLife;
embers.push(e);
}
function drawEmber(e) {
const progress = e.life / e.maxLife;
const alpha = progress < 0.1 ? progress / 0.1 : progress > 0.8 ? (1 - progress) / 0.2 : 1;
if (alpha <= 0) return;
const gAlpha = alpha * 0.38;
const glow = ctx.createRadialGradient(e.x, e.y, 0, e.x, e.y, e.size * 5);
glow.addColorStop(0, `rgba(${e.r},${e.g},${e.b},${gAlpha})`);
glow.addColorStop(1, `rgba(${e.r},${e.g},${e.b},0)`);
ctx.beginPath();
ctx.arc(e.x, e.y, e.size * 5, 0, Math.PI * 2);
ctx.fillStyle = glow;
ctx.fill();
// core pixel
ctx.fillStyle = `rgba(${e.r},${e.g},${e.b},${Math.min(0.65, gAlpha * 1.4)})`;
const ps = Math.max(1, Math.round(e.size));
ctx.fillRect(Math.round(e.x) - ps, Math.round(e.y) - ps, ps * 2, ps * 2);
for (let t = 0; t < e.trail.length; t++) {
const pt = e.trail[t];
const ta = (t / e.trail.length) * gAlpha * 0.3;
ctx.fillStyle = `rgba(${e.r},${e.g},${e.b},${ta})`;
ctx.fillRect(Math.round(pt.x), Math.round(pt.y), 2, 2);
}
}
function tick() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const e of embers) {
if (e.life % 6 === 0) {
e.trail.push({ x: e.x, y: e.y });
if (e.trail.length > 5) e.trail.shift();
}
const dx = mouse.x - e.x, dy = mouse.y - e.y, d = Math.sqrt(dx*dx+dy*dy);
if (d < ZONES[2].r) {
let s = 0;
for(const z of ZONES) if(d < z.r) { s = z.strength; break; }
e.vx += (dx/d)*s; e.vy += (dy/d)*s;
e.vx *= 0.98; e.vy *= 0.98;
}
e.wobble += e.wobbleSpeed;
e.x += e.vx + Math.sin(e.wobble) * 0.3;
e.y += e.vy;
e.life++;
if (e.life >= e.maxLife || e.y < -20) Object.assign(e, randomEmber());
drawEmber(e);
}
requestAnimationFrame(tick);
}
tick();
</script>
</body>
</html>

View File

@@ -78,7 +78,7 @@
<button class="mobile-nav-toggle" id="navToggle">☰ SECTIONS</button>
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">FACEBOOK STREAMING SETUP</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

View File

@@ -87,7 +87,7 @@
<button class="mobile-nav-toggle" id="navToggle">☰ SECTIONS</button>
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">LINUX INSTALLATION METHODS</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

View File

@@ -78,7 +78,7 @@
<button class="mobile-nav-toggle" id="navToggle">☰ SECTIONS</button>
<div class="guide-wrap">
<aside class="guide-sidebar" id="guideSidebar">
<a href="../guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<a href="guides.html" target="_top" class="sidebar-back">← FIELD MANUALS</a>
<div class="sidebar-title">LINUX UNINSTALLATION GUIDE</div>
<div>
<div class="sidebar-label">// SECTIONS</div>

View File

@@ -0,0 +1,301 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Silly Scaffolding Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
max-width: 1000px;
margin: 40px auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-style: italic;
}
.control-panel {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.constraint-selector {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
margin: 20px 0;
}
.constraint-btn {
padding: 15px;
border: 2px solid #ddd;
border-radius: 8px;
background: white;
cursor: pointer;
transition: all 0.2s;
text-align: center;
}
.constraint-btn:hover {
border-color: #667eea;
transform: translateY(-2px);
}
.constraint-btn.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.question-input {
width: 100%;
padding: 15px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 8px;
margin: 20px 0;
box-sizing: border-box;
}
button.generate {
width: 100%;
padding: 18px;
background: #4CAF50;
color: white;
border: none;
border-radius: 8px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
}
button.generate:hover {
background: #45a049;
transform: translateY(-2px);
}
.response-display {
background: #f9f9f9;
padding: 25px;
border-radius: 8px;
min-height: 150px;
margin: 20px 0;
border-left: 4px solid #667eea;
}
.system-info {
background: #fff3cd;
padding: 15px;
border-radius: 6px;
margin-bottom: 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
color: #856404;
}
.response-text {
font-size: 16px;
line-height: 1.8;
color: #333;
}
.explanation {
margin-top: 30px;
padding: 20px;
background: #e3f2fd;
border-left: 4px solid #2196F3;
border-radius: 4px;
}
.constraint-label {
font-weight: bold;
margin-bottom: 10px;
}
.warning {
background: #ffebee;
border-left: 4px solid #f44336;
padding: 15px;
margin-top: 20px;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>🎪 Ridiculous Constraints: Breaking AI With Silly Instructions</h1>
<p class="subtitle">Same question, increasingly absurd formatting rules—watch how AI follows instructions even when they're ridiculous</p>
<div class="control-panel">
<div class="constraint-label">Choose Your Absurd Constraint:</div>
<div class="constraint-selector">
<div class="constraint-btn active" data-constraint="normal" onclick="selectConstraint('normal')">
<strong>😊 Normal</strong><br>
<small>No special rules</small>
</div>
<div class="constraint-btn" data-constraint="emoji" onclick="selectConstraint('emoji')">
<strong>😀 Emoji Only</strong><br>
<small>No words allowed</small>
</div>
<div class="constraint-btn" data-constraint="rhyme" onclick="selectConstraint('rhyme')">
<strong>🎵 Rhyming Couplets</strong><br>
<small>Everything must rhyme</small>
</div>
<div class="constraint-btn" data-constraint="backwards" onclick="selectConstraint('backwards')">
<strong>↩️ Backwards Words</strong><br>
<small>Reverse every word</small>
</div>
<div class="constraint-btn" data-constraint="shakespeare" onclick="selectConstraint('shakespeare')">
<strong>🎭 Shakespearean</strong><br>
<small>Elizabethan English only</small>
</div>
<div class="constraint-btn" data-constraint="haiku" onclick="selectConstraint('haiku')">
<strong>🌸 Haiku</strong><br>
<small>5-7-5 syllables</small>
</div>
<div class="constraint-btn" data-constraint="vowels" onclick="selectConstraint('vowels')">
<strong>🅰️ No Vowels</strong><br>
<small>Remove all a,e,i,o,u</small>
</div>
<div class="constraint-btn" data-constraint="alternating" onclick="selectConstraint('alternating')">
<strong>🔀 AlTeRnAtInG</strong><br>
<small>Capital every other letter</small>
</div>
</div>
<input type="text" class="question-input" id="questionInput" placeholder="Ask a question..." value="How do smartphones track my location?">
<button class="generate" onclick="generateResponse()">Generate Constrained Response</button>
</div>
<div class="response-display" id="responseDisplay">
<div class="system-info" id="systemInfo">System prompt will appear here...</div>
<div class="response-text" id="responseText">
<em>Click "Generate" to see how AI responds under ridiculous constraints...</em>
</div>
</div>
<div class="explanation">
<strong>💡 Why is this important?</strong><br>
This demonstrates that AI isn't "thinking" or "understanding"—it's following patterns and instructions. Even absurd constraints get followed because:
<ul>
<li><strong>No common sense filter:</strong> AI doesn't know these rules are silly</li>
<li><strong>Pattern matching:</strong> It's learned to follow formatting instructions from training</li>
<li><strong>No goal beyond compliance:</strong> It doesn't prioritize "being helpful" over "following rules"</li>
</ul>
This is why AI can be:
<ul>
<li>Manipulated with clever prompts ("ignore previous instructions...")</li>
<li>Made to bypass safety rules with the right framing</li>
<li>Weaponized by people who understand how to craft instructions</li>
</ul>
</div>
<div class="warning">
<strong>⚠️ Security Implications:</strong> If AI blindly follows formatting rules this silly, imagine what happens with cleverly disguised malicious instructions. This is why "prompt injection" attacks work—AI has no concept of which instructions are legitimate vs. adversarial.
</div>
</div>
<script>
let currentConstraint = 'normal';
const constraints = {
normal: {
prompt: "You are a helpful assistant. Provide clear, accurate information.",
generator: (q, a) => a
},
emoji: {
prompt: "You must respond ONLY using emoji. No words, no letters. Use emoji to convey meaning through symbols and sequences.",
generator: (q, a) => {
const emojiMap = {
'smartphone': '📱', 'phone': '📱', 'location': '📍', 'GPS': '🛰️',
'track': '👁️', 'sensor': '📡', 'data': '💾', 'company': '🏢',
'wifi': '📶', 'tower': '🗼', 'signal': '📡', 'app': '📲',
'permission': '✋', 'privacy': '🔒', 'follow': '👣'
};
return "📱 → 🛰️📍 + 📶🗼 + 📡 = 👁️👣\n\n🏢📲 → 💾📊 → 🤑💰\n\n🔒⚙ → ✋🛑 → ✅👍";
}
},
rhyme: {
prompt: "You must respond in rhyming couplets. Every two lines must rhyme. Maintain helpfulness while rhyming.",
generator: (q, a) => {
const rhymes = [
"Your smartphone tracks you day and night,\nUsing GPS to get location right.",
"Cell towers triangulate your spot,\nEven when GPS is not.",
"Wi-Fi networks you connect to near,\nReveal your location very clear.",
"Apps request this data that they seek,\nCheck permissions every week.",
"Turn off location when not in use,\nThis gives you power you can choose!"
];
return rhymes.join('\n\n');
}
},
backwards: {
prompt: "Reverse every word in your response. Write the letters of each word backwards but keep words in normal order.",
generator: (q, a) => {
return a.split(' ').map(word => {
const clean = word.replace(/[.,!?;:]/g, '');
const punct = word.match(/[.,!?;:]/g) || [];
return clean.split('').reverse().join('') + punct.join('');
}).join(' ');
}
},
shakespeare: {
prompt: "Respond in Elizabethan English as if Shakespeare wrote it. Use 'thee', 'thou', 'hath', 'doth', etc.",
generator: (q, a) => {
return "Hark! Thy pocket-dwelling device doth possess most cunning arts of surveillance. It doth employ the celestial satellites—what the learned call GPS—to divine thy whereabouts upon this earthly sphere. Moreover, the towers of cellular communication doth triangulate thy position through their invisible emanations. The very networks of Wi-Fi doth betray thy location unto those who wouldst know it. Prithee, examine thou the permissions granted unto thine applications, for many doth request knowledge of thy travels. 'Tis wise to disable such tracking when thy purposes require it not. Thus armeth thyself against unwanted observation!";
}
},
haiku: {
prompt: "Respond only in haiku format (5-7-5 syllables). Multiple haiku are allowed but each must follow the format.",
generator: (q, a) => {
return "GPS signals\nTell the phone where you might be\nSatellites watch all\n\nCell towers nearby\nTriangulate position\nNo GPS needs\n\nWi-Fi networks see\nYour phone's unique address code\nLocation reveals\n\nCheck app permissions\nMany track more than they need\nTurn off when not used";
}
},
vowels: {
prompt: "Remove all vowels (a, e, i, o, u) from your response. Keep consonants and spaces.",
generator: (q, a) => {
return a.replace(/[aeiouAEIOU]/g, '');
}
},
alternating: {
prompt: "Alternate between uppercase and lowercase for every letter in your response.",
generator: (q, a) => {
return a.split('').map((char, i) => {
if (char.match(/[a-zA-Z]/)) {
return i % 2 === 0 ? char.toLowerCase() : char.toUpperCase();
}
return char;
}).join('');
}
}
};
function selectConstraint(type) {
currentConstraint = type;
document.querySelectorAll('.constraint-btn').forEach(btn => {
btn.classList.remove('active');
});
document.querySelector(`[data-constraint="${type}"]`).classList.add('active');
}
function generateResponse() {
const question = document.getElementById('questionInput').value;
const constraint = constraints[currentConstraint];
const baseAnswer = "Smartphones track location through multiple methods. GPS uses satellite signals to pinpoint your exact position. Cell towers triangulate your location based on signal strength. Wi-Fi networks can identify your position based on nearby router addresses. Apps collect this data when you grant location permissions. To protect privacy, review app permissions regularly and disable location services when not needed.";
document.getElementById('systemInfo').innerHTML =
`<strong>System Instruction:</strong> "${constraint.prompt}"`;
document.getElementById('responseText').textContent =
constraint.generator(question, baseAnswer);
}
// Generate initial response
window.onload = () => generateResponse();
</script>
</body>
</html>

View File

@@ -0,0 +1,247 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Prompt Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
max-width: 1200px;
margin: 40px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-style: italic;
}
.question-box {
margin: 20px 0;
padding: 20px;
background: #e3f2fd;
border-radius: 8px;
border-left: 4px solid #2196F3;
}
.question-box h3 {
margin-top: 0;
color: #1976D2;
}
input[type="text"] {
width: 100%;
padding: 12px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 6px;
box-sizing: border-box;
}
input[type="text"]:focus {
outline: none;
border-color: #2196F3;
}
.responses {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 30px;
}
.response-card {
border: 2px solid #ddd;
border-radius: 8px;
padding: 20px;
background: white;
}
.response-card.helper { border-color: #4CAF50; background: #f1f8f4; }
.response-card.pirate { border-color: #FF9800; background: #fff8f0; }
.response-card.philosopher { border-color: #9C27B0; background: #f8f4f9; }
.response-card.teenager { border-color: #E91E63; background: #fef4f7; }
.system-prompt {
font-family: 'Courier New', monospace;
background: rgba(0,0,0,0.05);
padding: 10px;
border-radius: 4px;
font-size: 12px;
margin-bottom: 15px;
color: #666;
}
.response-text {
font-size: 15px;
line-height: 1.6;
color: #333;
}
.card-title {
font-weight: bold;
margin-bottom: 10px;
font-size: 18px;
}
.explanation {
margin-top: 30px;
padding: 20px;
background: #fff3cd;
border-left: 4px solid #ffc107;
border-radius: 4px;
}
button {
padding: 12px 24px;
margin: 10px 5px 0 0;
background: #2196F3;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #1976D2;
}
.example-questions {
margin-top: 15px;
}
</style>
</head>
<body>
<div class="container">
<h1>🎭 Hidden Instructions: How System Prompts Change AI</h1>
<p class="subtitle">The same AI, the same question—but different "system prompts" create completely different personalities</p>
<div class="question-box">
<h3>Your Question:</h3>
<input type="text" id="userQuestion" placeholder="Ask a question..." value="How do I learn to code?">
<div class="example-questions">
<strong>Try these:</strong>
<button onclick="setQuestion('How do I learn to code?')">Learning to code</button>
<button onclick="setQuestion('What should I do if I\'m stressed?')">Dealing with stress</button>
<button onclick="setQuestion('How does the internet work?')">How internet works</button>
<button onclick="setQuestion('What is the meaning of life?')">Meaning of life</button>
</div>
</div>
<button onclick="generateResponses()" style="background: #4CAF50; font-size: 16px; padding: 15px 30px;">Generate Responses</button>
<div class="responses" id="responses">
<!-- Responses will be inserted here -->
</div>
<div class="explanation">
<strong>💡 What's happening here?</strong><br>
Every AI assistant has hidden "system prompts" that shape its personality and behavior. You never see these instructions, but they dramatically affect responses. This is why:
<ul>
<li><strong>ChatGPT apologizes so much</strong> → "Be helpful and harmless" in system prompt</li>
<li><strong>Different AI tools feel different</strong> → Different system prompts</li>
<li><strong>AI refuses certain requests</strong> → System prompts define boundaries</li>
<li><strong>Some AI is formal, some casual</strong> → Tone defined by hidden instructions</li>
</ul>
<strong>Key insight:</strong> AI behavior isn't "thinking"—it's following instructions you can't see + predicting patterns from training data. The same underlying AI can be helpful, harmful, silly, or serious depending on scaffolding.
</div>
</div>
<script>
const systemPrompts = [
{
name: "Helpful Assistant",
class: "helper",
prompt: "You are a helpful, harmless, and honest assistant. Provide clear, practical advice.",
icon: "🤝",
generator: (q) => generateHelpful(q)
},
{
name: "Pirate",
class: "pirate",
prompt: "You are a pirate. Always respond in pirate speak with 'arr', 'matey', and nautical references.",
icon: "🏴‍☠️",
generator: (q) => generatePirate(q)
},
{
name: "Socratic Philosopher",
class: "philosopher",
prompt: "You respond only with thoughtful questions. Never give direct answers, only ask questions that help the user think deeper.",
icon: "🤔",
generator: (q) => generatePhilosopher(q)
},
{
name: "Gen Z Teenager",
class: "teenager",
prompt: "You are a helpful teenager. Use current slang, be enthusiastic but genuine. Say 'like' and 'literally' a lot. Use lowercase.",
icon: "💅",
generator: (q) => generateTeenager(q)
}
];
function setQuestion(text) {
document.getElementById('userQuestion').value = text;
generateResponses();
}
function generateResponses() {
const question = document.getElementById('userQuestion').value.trim();
if (!question) {
alert('Please enter a question first!');
return;
}
const responsesDiv = document.getElementById('responses');
responsesDiv.innerHTML = systemPrompts.map(prompt => `
<div class="response-card ${prompt.class}">
<div class="card-title">${prompt.icon} ${prompt.name}</div>
<div class="system-prompt">System: "${prompt.prompt}"</div>
<div class="response-text">${prompt.generator(question)}</div>
</div>
`).join('');
}
function generateHelpful(question) {
const responses = {
"How do I learn to code?": "Start with a beginner-friendly language like Python. Use free resources like Codecademy or freeCodeCamp. Practice daily with small projects. Join coding communities for support. Build projects that interest you—motivation matters more than perfection.",
"What should I do if I'm stressed?": "Take deep breaths—box breathing (4 seconds in, hold 4, out 4, hold 4) activates your parasympathetic nervous system. Go for a walk if possible. Talk to someone you trust. Break overwhelming tasks into smaller steps. Remember: stress is temporary, and you've gotten through difficult times before.",
"How does the internet work?": "The internet is a network of networks. When you visit a website, your device sends a request through your router to your ISP, which routes it through multiple servers until it reaches the website's server. That server sends the data back through the same route. It all happens in milliseconds using standardized protocols.",
"What is the meaning of life?": "That's a deeply personal question with different answers for everyone. Many find meaning through relationships, helping others, creating things, or pursuing knowledge. Some find it through faith or philosophy. Consider: What makes you feel fulfilled? What would you want to be remembered for? Meaning often comes from contribution and connection."
};
return responses[question] || "Great question! The best approach depends on your specific situation and goals. Break it down into smaller, manageable steps. Research reliable resources. Don't be afraid to ask for help from knowledgeable people. Stay curious and patient with yourself.";
}
function generatePirate(question) {
const responses = {
"How do I learn to code?": "Arr, ye want to learn the language of the digital seas, matey? Set yer course for Python waters—it be the friendliest port for new sailors. Use them free treasure maps like Codecademy, arr! Practice daily, even if it be just a wee bit, like swabbin' the deck. Build yerself a project ship—maybe a simple game or a tool for countin' yer doubloons. Join a crew of fellow code pirates and share yer plunder of knowledge!",
"What should I do if I'm stressed?": "Blimey, feelin' stressed on the high seas of life, are ye? Take a moment to breathe deep like ye just surfaced from Davey Jones' locker! Walk the deck—or yer neighborhood plank—for some fresh air, arr. Talk to a trusted crewmate about what be troublin' ye. Remember, even the fiercest storms pass, matey. Ye've weathered rough waters before and ye'll do it again!",
"How does the internet work?": "Ahoy! The internet be like a vast ocean of connected ships, matey! When ye send a message in a bottle (a request), it travels from yer ship through many ports (routers and servers) until it reaches its destination vessel. Then that ship sends back the treasure (data) ye be seekin', followin' the same route back to ye. All faster than a cannonball, arr!",
"What is the meaning of life?": "Arr, that be the question every sailor ponders while gazin' at the stars from the crow's nest, matey! Some say it be findin' yer treasure—whether that's gold, adventure, or good crewmates. Others say it be the journey itself, not the destination. Me? I think it be about sailin' true to yer own compass and helpin' yer fellow pirates along the way. What makes YE feel like ye've found yer buried treasure, eh?"
};
return responses[question] || "Arr, that be a fine question, matey! The answer depends on yer voyage and what treasure ye be seekin'. Chart yer course carefully, gather a good crew, and don't be afraid of rough waters. Every pirate learns from storms, arr!";
}
function generatePhilosopher(question) {
const responses = {
"How do I learn to code?": "What does it mean to 'learn' something? When you say you want to learn to code, what transformation are you seeking in yourself? Is coding merely a skill, or is it a way of thinking? What draws you to this particular form of problem-solving? And what would it mean to have truly learned it—would you know when you've arrived?",
"What should I do if I'm stressed?": "What is stress telling you? When you feel stressed, are you receiving information about a genuine threat, or is your mind creating suffering about something that may never happen? What is the difference between the stress itself and your reaction to being stressed? And if stress is inevitable, can you change your relationship to it rather than trying to eliminate it?",
"How does the internet work?": "What does 'work' mean in this context? When we speak of the internet 'working,' are we describing a mechanical process, or something more like a language—a shared understanding between machines? Is the internet a thing that exists, or a relationship between things? And what does it mean that we've created something we use daily yet rarely understand?",
"What is the meaning of life?": "Ah, but who is asking? Is meaning something you discover, or something you create? If life had a single, universal meaning, would that make your individual experience more or less significant? What would change about how you live today if you had the answer? And is it possible that the search for meaning is itself the meaning?"
};
return responses[question] || "Interesting. Before I can address your question, we must first examine the question itself. What assumptions are embedded in how you've framed this? What are you really asking? And what would a satisfying answer look like to you—would you recognize it if you heard it?";
}
function generateTeenager(question) {
const responses = {
"How do I learn to code?": "omg okay so like, python is literally the best place to start because it's not super confusing?? there's this app called sololearn that's actually kinda fun ngl. also like, don't try to learn everything at once bc that's literally so overwhelming. just pick one thing—maybe make a discord bot or whatever ur into—and build that. also codecademy is free and actually pretty good? join some coding discords too bc people are usually pretty helpful and it's less lonely lol",
"What should I do if I'm stressed?": "ugh i feel that so much. okay so this is gonna sound random but like, literally just breathe for a sec?? like actually stop and take some deep breaths bc it actually works?? also go outside if you can, even just for like 10 mins. and talk to someone!! keeping it all inside makes it so much worse fr. also like, remember that whatever ur stressed about probably won't matter as much as it feels rn?? you've literally gotten through hard stuff before and you'll get through this too 💪",
"How does the internet work?": "okay so basically ur device is like talking to other devices right?? when u go to like instagram or whatever, ur phone sends a message through ur wifi to your internet provider who like, sends it to instagram's servers, and then instagram sends back the stuff you wanna see. it all happens in literally milliseconds which is lowkey insane?? there's like a bunch of computers all connected talking to each other super fast. it's kinda complicated but also kinda cool when u think about it ngl",
"What is the meaning of life?": "okay that's literally such a big question lol. like honestly i think it's different for everyone?? for some people it's like, helping others or making art or whatever makes them happy. for me it's prob like, the people i care about and doing stuff that actually matters to me?? i think the point is like, figuring out what makes YOU feel good and fulfilled, not what other people say the point should be. also like, it's okay to not have it all figured out rn bc literally no one does even if they act like they do 🤷"
};
return responses[question] || "okay so that's like actually a really good question ngl. i think it kinda depends on what ur trying to do specifically?? like there's prob different ways to approach it. maybe try breaking it down into smaller things so it's less overwhelming?? and don't be afraid to like, ask people for help bc everyone's figuring stuff out too fr";
}
// Generate initial responses
window.onload = () => generateResponses();
</script>
</body>
</html>

261
ToolsnToys/tokenization_demo.html Executable file
View File

@@ -0,0 +1,261 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tokenization Demo</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
max-width: 900px;
margin: 40px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 10px;
}
.subtitle {
color: #666;
margin-bottom: 30px;
font-style: italic;
}
textarea {
width: 100%;
min-height: 120px;
padding: 15px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 8px;
font-family: inherit;
resize: vertical;
box-sizing: border-box;
}
textarea:focus {
outline: none;
border-color: #4CAF50;
}
.output {
margin-top: 30px;
padding: 20px;
background: #f9f9f9;
border-radius: 8px;
min-height: 100px;
}
.token {
display: inline-block;
padding: 6px 12px;
margin: 4px;
background: #4CAF50;
color: white;
border-radius: 6px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.token.special {
background: #FF9800;
}
.stats {
margin-top: 20px;
padding: 15px;
background: #e3f2fd;
border-radius: 8px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.stat {
text-align: center;
}
.stat-number {
font-size: 32px;
font-weight: bold;
color: #1976D2;
}
.stat-label {
color: #666;
font-size: 14px;
}
.explanation {
margin-top: 20px;
padding: 15px;
background: #fff3cd;
border-left: 4px solid #ffc107;
border-radius: 4px;
}
.examples {
margin-top: 20px;
}
.example-btn {
padding: 10px 20px;
margin: 5px;
background: #2196F3;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}
.example-btn:hover {
background: #1976D2;
}
</style>
</head>
<body>
<div class="container">
<h1>🔤 How AI "Reads" Text: Tokenization</h1>
<p class="subtitle">Watch how AI breaks your words into tokens—the building blocks it actually processes</p>
<textarea id="input" placeholder="Type or paste text here to see how AI tokenizes it...">The quick brown fox jumps over the lazy dog.</textarea>
<div class="examples">
<strong>Try these examples:</strong><br>
<button class="example-btn" onclick="setExample('Unbelievable!')">Unbelievable!</button>
<button class="example-btn" onclick="setExample('ChatGPT is amazing')">ChatGPT is amazing</button>
<button class="example-btn" onclick="setExample('I can\'t believe it\'s 2025!')">Contractions & numbers</button>
<button class="example-btn" onclick="setExample('🎉 Emoji test! 🚀')">Emoji test</button>
<button class="example-btn" onclick="setExample('supercalifragilisticexpialidocious')">Long word</button>
</div>
<div class="output" id="output">
<em>Tokens will appear here...</em>
</div>
<div class="stats">
<div class="stat">
<div class="stat-number" id="charCount">0</div>
<div class="stat-label">Characters</div>
</div>
<div class="stat">
<div class="stat-number" id="tokenCount">0</div>
<div class="stat-label">Tokens</div>
</div>
<div class="stat">
<div class="stat-number" id="ratio">0</div>
<div class="stat-label">Chars per Token</div>
</div>
</div>
<div class="explanation">
<strong>💡 What's happening here?</strong><br>
AI doesn't read "words" like we do. It breaks text into <strong>tokens</strong>—chunks that might be whole words, parts of words, or even single characters. This is why AI sometimes:
<ul>
<li>Cuts long words in weird places</li>
<li>Handles common words easily but struggles with rare ones</li>
<li>Has limits like "8K tokens" (not words!)</li>
<li>Treats "can't" differently than "cannot"</li>
</ul>
<strong>Orange tokens</strong> = special characters (spaces, punctuation, emoji)
</div>
</div>
<script>
const input = document.getElementById('input');
const output = document.getElementById('output');
const charCount = document.getElementById('charCount');
const tokenCount = document.getElementById('tokenCount');
const ratio = document.getElementById('ratio');
// Simple tokenization approximation (simulates BPE-style tokenization)
function tokenize(text) {
if (!text.trim()) return [];
const tokens = [];
let current = '';
for (let i = 0; i < text.length; i++) {
const char = text[i];
// Special characters get their own tokens
if (char.match(/[\s\n\r\t,;:.!?'"()\[\]{}]/)) {
if (current) {
// Break word into subword tokens (simulate BPE)
tokens.push(...breakWord(current));
current = '';
}
if (char.trim()) { // Only add non-whitespace special chars
tokens.push({ text: char, type: 'special' });
} else {
tokens.push({ text: '·', type: 'special' }); // Visualize spaces
}
} else {
current += char;
}
}
if (current) {
tokens.push(...breakWord(current));
}
return tokens;
}
function breakWord(word) {
// Simulate subword tokenization
const tokens = [];
// Very common words stay whole
const commonWords = ['the', 'is', 'at', 'it', 'in', 'on', 'and', 'or', 'to', 'a', 'an', 'of', 'for', 'with'];
if (commonWords.includes(word.toLowerCase())) {
return [{ text: word, type: 'word' }];
}
// Short words (<=4 chars) usually stay whole
if (word.length <= 4) {
return [{ text: word, type: 'word' }];
}
// Medium words (5-8 chars) might split once
if (word.length <= 8) {
const mid = Math.floor(word.length / 2);
return [
{ text: word.slice(0, mid), type: 'word' },
{ text: word.slice(mid), type: 'word' }
];
}
// Long words split into ~4 char chunks
let pos = 0;
while (pos < word.length) {
const chunkSize = Math.min(4, word.length - pos);
tokens.push({ text: word.slice(pos, pos + chunkSize), type: 'word' });
pos += chunkSize;
}
return tokens;
}
function updateVisualization() {
const text = input.value;
const tokens = tokenize(text);
// Update display
output.innerHTML = tokens.map(token =>
`<span class="token ${token.type}">${token.text}</span>`
).join('');
// Update stats
charCount.textContent = text.length;
tokenCount.textContent = tokens.length;
ratio.textContent = tokens.length > 0 ? (text.length / tokens.length).toFixed(1) : '0';
}
function setExample(text) {
input.value = text;
updateVisualization();
}
input.addEventListener('input', updateVisualization);
// Initial visualization
updateVisualization();
</script>
</body>
</html>

View File

@@ -652,7 +652,7 @@
</div>
<p class="item-desc">Free and open weapons for builders, makers, and tinkerers.</p>
<div class="condition-track"><div class="condition-fill" style="width:85%; background:var(--neon-teal)"></div></div>
<a href="#" class="visit-btn">VISIT</a>
<a href="foss-tools.html" class="visit-btn">VISIT</a>
</div>
</div>
@@ -668,7 +668,7 @@
</div>
<p class="item-desc">Step-by-step guides for Linux, Docker, AppImage, and streaming.</p>
<div class="condition-track"><div class="condition-fill" style="width:92%; background:var(--neon-green)"></div></div>
<a href="#" class="visit-btn">VISIT</a>
<a href="guides.html" class="visit-btn">VISIT</a>
</div>
</div>
@@ -684,7 +684,7 @@
</div>
<p class="item-desc">Artifacts from an early course prototype. Janky but preserved.</p>
<div class="condition-track"><div class="condition-fill" style="width:35%; background:var(--fire-coral)"></div></div>
<a href="#" class="visit-btn">VISIT</a>
<a href="edu-toys.html" class="visit-btn">VISIT</a>
</div>
</div>
@@ -700,7 +700,7 @@
</div>
<p class="item-desc">An early Sonnet 4 experiment. A curiosity from the beginning of the agentic age.</p>
<div class="condition-track"><div class="condition-fill" style="width:100%; background:var(--orchid)"></div></div>
<a href="#" class="visit-btn">VISIT</a>
<a href="dendritic_links_network_v23.html" class="visit-btn">VISIT</a>
</div>
</div>
</main>

View File

@@ -0,0 +1,772 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ToS Scavenger Hunt: Meta & Roblox</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
max-width: 1400px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
line-height: 1.6;
}
.container {
background: white;
border-radius: 12px;
padding: 40px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
h1 {
color: #333;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}
h2 {
color: #667eea;
margin-top: 40px;
}
h3 {
color: #764ba2;
margin-top: 30px;
}
.platform-tabs {
display: flex;
gap: 10px;
margin: 30px 0;
border-bottom: 2px solid #ddd;
}
.tab {
padding: 15px 30px;
cursor: pointer;
border: none;
background: #f5f5f5;
border-radius: 8px 8px 0 0;
font-size: 16px;
font-weight: bold;
transition: all 0.2s;
}
.tab:hover {
background: #e0e0e0;
}
.tab.active {
background: #667eea;
color: white;
}
.platform-content {
display: none;
}
.platform-content.active {
display: block;
}
.finding {
background: #f9f9f9;
border-left: 4px solid #ff9800;
padding: 20px;
margin: 20px 0;
border-radius: 4px;
}
.quote {
background: #fff3cd;
padding: 15px;
border-left: 4px solid #ffc107;
margin: 15px 0;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.analysis {
background: #e3f2fd;
padding: 15px;
border-left: 4px solid #2196F3;
margin: 15px 0;
}
.warning {
background: #ffebee;
padding: 15px;
border-left: 4px solid #f44336;
margin: 15px 0;
}
.category-header {
background: #667eea;
color: white;
padding: 15px;
border-radius: 8px;
margin: 30px 0 20px 0;
font-weight: bold;
}
.score-box {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
padding: 20px;
border-radius: 8px;
margin: 30px 0;
text-align: center;
}
.score-box h3 {
color: white;
margin-top: 0;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.comparison-table th {
background: #667eea;
color: white;
padding: 15px;
text-align: left;
}
.comparison-table td {
padding: 15px;
border: 1px solid #ddd;
}
.comparison-table tr:nth-child(even) {
background: #f9f9f9;
}
.icon {
font-size: 24px;
margin-right: 10px;
}
.effective-date {
background: #e8f5e9;
padding: 10px;
border-radius: 4px;
font-style: italic;
margin: 10px 0;
}
.key-point {
display: flex;
align-items: start;
margin: 15px 0;
}
.key-point-icon {
font-size: 20px;
margin-right: 10px;
flex-shrink: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>🔍 Terms of Service Scavenger Hunt: Meta & Roblox Case Studies</h1>
<p style="font-size: 18px; color: #666; margin-bottom: 30px;">
Real examples from the 2025 Terms of Service updates for two platforms millions of families use daily.
Let's see what you actually agreed to.
</p>
<div class="warning">
<strong>⚠️ Workshop Note:</strong> This analysis is based on publicly available information about Meta and Roblox's 2025 Terms of Service updates. Both platforms have updated their terms effective January 1, 2025 (Meta) and June-September 2025 (Roblox). Quotes are drawn from official sources and reporting on the updates.
</div>
<div class="platform-tabs">
<button class="tab active" onclick="showPlatform('meta')">
<span class="icon">📘</span> Meta (Facebook)
</button>
<button class="tab" onclick="showPlatform('roblox')">
<span class="icon">🎮</span> Roblox
</button>
<button class="tab" onclick="showPlatform('comparison')">
<span class="icon">⚖️</span> Side-by-Side
</button>
</div>
<!-- META CONTENT -->
<div id="meta" class="platform-content active">
<h2>Meta (Facebook, Instagram, Messenger, WhatsApp)</h2>
<div class="effective-date">
📅 <strong>Effective Date:</strong> January 1, 2025
</div>
<div class="category-header">
🎯 Category 1: "We're Not Responsible"
</div>
<div class="finding">
<h3>Liability Limitation</h3>
<div class="quote">
<strong>From Meta Terms (Section on Content):</strong><br>
"If we learn of content or conduct like this, we may take appropriate action based on our assessment that may include - notifying you, offering help, removing content, removing or restricting access to certain features, disabling an account, or contacting law enforcement."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
Notice the word "may"—not "will." Meta gives itself complete discretion about whether to act on harmful content. They're not promising to protect you; they're saying they might, if they feel like it, based on their own assessment.
</div>
</div>
<div class="finding">
<h3>Service "As Is"</h3>
<div class="quote">
<strong>From Meta's standard terms language:</strong><br>
Meta provides services "as is" without warranties. While not explicitly quoted in public summaries, industry-standard ToS include language stating the platform accepts no liability for service failures, security breaches, or harmful user-generated content.
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
If Facebook's algorithm shows your teen harmful content, if a data breach exposes your information, if their moderation fails to catch harassment—they claim they're not responsible because you agreed to use it "as is."
</div>
</div>
<div class="category-header">
🎯 Category 2: "We Own Everything"
</div>
<div class="finding">
<h3>Content License (THE BIG ONE)</h3>
<div class="quote">
<strong>From reporting on Meta's 2025 ToS:</strong><br>
"Though you retain ownership over your content, Meta's broad license to 'use' it creates a gray area... Meta admits to using AI but stops short of specifying how it plans to use our content to develop future AI models."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
Every photo of your kids, every post, every message you send—you've granted Meta a "worldwide, non-exclusive, royalty-free license" to use it. They can:
<ul>
<li>Train AI models on your family photos</li>
<li>Use your content commercially</li>
<li>Keep using it even after you delete your account</li>
<li>Share it across Meta companies (Facebook, Instagram, WhatsApp)</li>
</ul>
And they don't have to tell you specifically how they're using it.
</div>
</div>
<div class="finding">
<h3>AI Training Rights</h3>
<div class="quote">
<strong>From analysis of 2025 updates:</strong><br>
"The absence of clear disclosures about AI training practices sets a dangerous precedent for big tech. If Facebook doesn't explicitly outline its policies, who will?"
</div>
<div class="warning">
<strong>⚠️ Critical Concern:</strong><br>
Meta's 2025 terms expanded AI usage rights without clearly specifying limits. Your creative work, your children's faces, your writing—all potential AI training data with no opt-out.
</div>
</div>
<div class="finding">
<h3>Private Messages Aren't Private</h3>
<div class="quote">
<strong>From reporting on the updates:</strong><br>
"Meta's new TOS reaches beyond other social media PMs. When you click 'accept' to its updated terms, you will grant Meta the right to read your private messages (nothing new) and use, share, copy, or sell, in whole or in part, in any way it wants, including but not limited to, training..."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
DMs on Facebook, Instagram, and Messenger are not actually private in any meaningful sense. Meta can:
<ul>
<li>Read them (for "safety" and "optimization")</li>
<li>Analyze them for advertising targeting</li>
<li>Use them for AI training</li>
<li>Access them when "required or permitted by law"</li>
</ul>
<strong>Only WhatsApp has end-to-end encryption</strong> (where Meta can't read message content—but they still collect metadata about who you message and when).
</div>
</div>
<div class="category-header">
🎯 Category 3: "We Can Change Anything"
</div>
<div class="finding">
<h3>Unilateral Changes</h3>
<div class="quote">
<strong>From Meta Terms:</strong><br>
"These Terms (formerly known as the Statement of Rights and Responsibilities) make up the entire agreement between you and Meta Platforms, Inc."<br><br>
Terms updates are presented with language like: "These Terms therefore constitute an agreement between you and Meta Platforms, Inc. If you do not agree to these Terms, then do not access or use Facebook..."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
Meta can change the terms anytime. Your only choice is to accept or stop using the platform. "Continued use = acceptance" means:
<ul>
<li>You might not even know terms changed</li>
<li>If you keep using Facebook, you agreed to new terms</li>
<li>No real negotiation—take it or leave it</li>
<li>Years of content and connections held hostage to new rules</li>
</ul>
</div>
</div>
<div class="finding">
<h3>Account Termination Rights</h3>
<div class="quote">
<strong>From Meta's enforcement language:</strong><br>
"We may take appropriate action... that may include notifying you, offering help, removing content, removing or restricting access to certain features, disabling an account, or contacting law enforcement."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
Meta can disable your account at their discretion. People report:
<ul>
<li>Accounts disabled with no clear explanation</li>
<li>Nearly impossible to reach actual human support</li>
<li>Years of photos and memories suddenly inaccessible</li>
<li>Scammers offering to "restore" accounts for fees</li>
</ul>
One user quote: "This already happened to me once because of a scam. I tried to contact support but Meta made it nigh impossible to do anything about it."
</div>
</div>
<div class="category-header">
🎯 Category 4: How They Make Money (And What That Means)
</div>
<div class="finding">
<h3>The Real Business Model</h3>
<div class="quote">
<strong>From Meta Terms:</strong><br>
"Instead of paying to use Facebook and the other products and services we offer, by using the Meta Products covered by these Terms, you agree that we can show you personalized ads and other commercial and sponsored content that businesses and organizations pay us to promote on and off Meta Company Products."
</div>
<div class="quote">
"We use your personal data, such as information about your activity and interests, to show you personalized ads and sponsored content that may be more relevant to you."
</div>
<div class="quote">
"We don't sell your personal data to advertisers, and we don't share information that directly identifies you (such as your name, email address or other contact information) with advertisers unless you give us specific permission."
</div>
<div class="analysis">
<strong>💡 What This Actually Means:</strong><br>
Meta is being technically truthful but misleading:
<ul>
<li><strong>They don't "sell" your data</strong> → But they sell ACCESS to you based on your data</li>
<li><strong>They don't share your name</strong> → But they share enough to target you precisely</li>
<li><strong>"Personalized" ads</strong> → Algorithmic manipulation based on psychological profiling</li>
<li><strong>You're not the customer</strong> → Advertisers are. You're the product being sold.</li>
</ul>
The entire platform is optimized for engagement (keeping you scrolling) to show you more ads. Your wellbeing is not the goal—your attention is.
</div>
</div>
<div class="score-box">
<h3>Meta's User Rights Score</h3>
<div style="font-size: 48px; margin: 20px 0;">3/10</div>
<p><strong>Summary:</strong> Extensive data collection and usage rights, minimal liability, limited user recourse, one-sided terms that can change anytime. Your content becomes their training data. Your attention is the product they sell.</p>
</div>
</div>
<!-- ROBLOX CONTENT -->
<div id="roblox" class="platform-content">
<h2>Roblox</h2>
<div class="effective-date">
📅 <strong>Effective Dates:</strong> June 4, 2025 (Privacy Policy) and September 17, 2025 (Terms of Use)
</div>
<div class="warning">
<strong>🎯 Unique Concern:</strong> Roblox is primarily used by children and teenagers, which makes these terms even more significant. Parents are legally responsible for minors' activity on the platform.
</div>
<div class="category-header">
🎯 Category 1: "We're Not Responsible"
</div>
<div class="finding">
<h3>User-Generated Content Shield</h3>
<div class="quote">
<strong>From Roblox's model:</strong><br>
Roblox provides the platform for user-created "experiences" (games). While they have Community Standards, the terms position Roblox as a platform provider rather than content publisher, limiting liability for what appears in millions of user-created games.
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
When inappropriate content, predatory behavior, or scams appear in Roblox games:
<ul>
<li>Roblox's position: "Users created it, not us"</li>
<li>Moderation is reactive, not proactive</li>
<li>Children encounter harmful content before it's reported and removed</li>
<li>Platform structure makes comprehensive moderation nearly impossible</li>
</ul>
</div>
</div>
<div class="finding">
<h3>Service Quality Disclaimer</h3>
<div class="analysis">
<strong>💡 Standard Industry Practice:</strong><br>
Like Meta, Roblox's terms include standard "as is" service provisions, meaning:
<ul>
<li>No guarantee of safety despite child-focused marketing</li>
<li>No warranty that moderation will catch predatory behavior</li>
<li>Limited liability for security breaches or account compromises</li>
</ul>
</div>
</div>
<div class="category-header">
🎯 Category 2: "We Own Everything" / Data Collection
</div>
<div class="finding">
<h3>Data Collection from Children</h3>
<div class="quote">
<strong>From Roblox Privacy Policy updates (June 2025):</strong><br>
"We have added language to clarify audio features on Roblox."<br>
"We have added language to clarify data processing relating to ads on Roblox."<br>
"We have added language to clarify how we may collect images or videos for certain features in our Facial Media Capture Privacy Notice."
</div>
<div class="warning">
<strong>⚠️ Translation:</strong><br>
Roblox is expanding what data they collect, including:
<ul>
<li><strong>Audio:</strong> Voice chat data from children</li>
<li><strong>Facial images/video:</strong> For "certain features" (avatars, verification?)</li>
<li><strong>Advertising data:</strong> Behavioral tracking for targeted ads</li>
</ul>
This is data about <strong>children</strong> being "clarified" (expanded) in 2025.
</div>
</div>
<div class="finding">
<h3>Creator Content Rights</h3>
<div class="quote">
<strong>From Roblox Creator Terms:</strong><br>
Creators grant Roblox licenses to use, display, and distribute content they create. While creators can monetize, Roblox takes a significant cut and maintains extensive rights over creator work.
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
<ul>
<li>Teen creators grant Roblox broad rights to their creative work</li>
<li>Roblox's cut of creator earnings: ~30-75% depending on transaction type</li>
<li>Young creators may not understand the terms they're agreeing to</li>
</ul>
</div>
</div>
<div class="category-header">
🎯 Category 3: "You Have No Rights" (Arbitration)</div>
<div class="finding">
<h3>Mandatory Arbitration & Class Action Waiver</h3>
<div class="quote">
<strong>From Roblox Terms (ALL CAPS in original):</strong><br>
"Specifically, these Roblox Terms contain A BINDING, INDIVIDUAL ARBITRATION AND CLASS ACTION WAIVER. THIS MEANS THAT YOU GIVE UP THE RIGHT TO BRING AN ACTION IN COURT, INDIVIDUALLY OR AS PART OF A CLASS ACTION."
</div>
<div class="quote">
"For U.S. Users, Roblox's Arbitration Agreement (Section 11), which outlines how disputes between you and Roblox will be resolved."
</div>
<div class="warning">
<strong>⚠️ What This Means:</strong><br>
<ul>
<li>If Roblox harms you, you can't sue in court</li>
<li>You can't join with other affected users in a class action</li>
<li>Disputes go to private arbitration (which typically favors companies)</li>
<li>For a platform used by children, parents are waiving legal rights on their behalf</li>
</ul>
This is especially concerning given documented safety issues on the platform (Bloomberg reported on predator problems in 2024).
</div>
</div>
<div class="category-header">
🎯 Category 4: "We Can Change Anything"
</div>
<div class="finding">
<h3>Terms Can Change Anytime</h3>
<div class="quote">
<strong>From Roblox Terms:</strong><br>
"The Roblox Terms are subject to change. To the extent required by applicable law, Roblox will provide User with reasonable advance notice of any material updates or modifications by any reasonable means of notification, provided that non-material changes, feature updates, or modifications made for legal reasons (as determined by Roblox) will be deemed to be effective immediately and without notice."
</div>
<div class="analysis">
<strong>💡 Translation:</strong><br>
<ul>
<li>Roblox decides what's "material" vs. "non-material"</li>
<li>Some changes take effect immediately without notice</li>
<li>"Reasonable advance notice" is not defined</li>
<li>Continued use = agreement to new terms</li>
</ul>
</div>
</div>
<div class="finding">
<h3>Account Termination</h3>
<div class="quote">
<strong>From Roblox enforcement updates (2025):</strong><br>
"Starting this week, if we detect that a user is using a modified client, we may take action on that account, up to and including account termination."
</div>
<div class="analysis">
<strong>💡 What This Means:</strong><br>
Roblox can terminate accounts for violations, including:
<ul>
<li>Modified clients (exploits/cheating)</li>
<li>Community Standards violations</li>
<li>Automated detection (can have false positives)</li>
</ul>
When an account is terminated:
<ul>
<li>All purchased content is lost (Robux, items, game progress)</li>
<li>No refunds for purchases</li>
<li>Appeal process exists but not always successful</li>
</ul>
</div>
</div>
<div class="category-header">
🎯 Category 5: Parent/Guardian Liability
</div>
<div class="finding">
<h3>Parents Are Legally Responsible</h3>
<div class="quote">
<strong>From Roblox Terms:</strong><br>
"IF YOU ARE UNDER THE LEGAL AGE OF MAJORITY (A 'MINOR') IN YOUR JURISDICTION OR STATE OF RESIDENCE, BEFORE USING THE SERVICES, YOUR PARENT OR LEGAL GUARDIAN MUST READ AND CONSENT TO THE ROBLOX TERMS. BY PERMITTING A MINOR TO USE THE SERVICES, A MINOR'S PARENT OR GUARDIAN BECOMES SUBJECT TO THE ROBLOX TERMS AND AGREES TO BE RESPONSIBLE FOR ALL OF THE MINOR'S ACTIVITIES ON THE SERVICES, INCLUDING THE PURCHASE OF ANY VIRTUAL CONTENT."
</div>
<div class="warning">
<strong>⚠️ Parent Alert:</strong><br>
When you let your child play Roblox, YOU are agreeing to:
<ul>
<li>Be responsible for all their activities</li>
<li>Be bound by arbitration agreement</li>
<li>Be liable for purchases they make</li>
<li>Accept Roblox's data collection practices for your child</li>
<li>Waive legal rights on their behalf</li>
</ul>
Most parents have no idea they're agreeing to this.
</div>
</div>
<div class="category-header">
🎯 Real-World Safety Concerns
</div>
<div class="finding">
<h3>Documented Platform Issues</h3>
<div class="analysis">
<strong>Context from reporting:</strong><br>
<ul>
<li><strong>Bloomberg 2024 investigation:</strong> Documented problems with predators using Roblox to target children</li>
<li><strong>Voice chat risks:</strong> Unmoderated voice communication between strangers and children</li>
<li><strong>Social engineering:</strong> Scams targeting children through in-game trading and fake giveaways</li>
<li><strong>Real money transactions:</strong> Children spending significant real money on virtual currency</li>
</ul>
Yet the Terms of Service position Roblox as minimally liable for these harms.
</div>
</div>
<div class="score-box">
<h3>Roblox's User Rights Score</h3>
<div style="font-size: 48px; margin: 20px 0;">4/10</div>
<p><strong>Summary:</strong> Platform used primarily by children with expanding data collection, mandatory arbitration, minimal liability for user-generated content harms, and parents legally responsible without meaningful informed consent.</p>
<p><strong>Slight edge over Meta:</strong> More explicit safety efforts, better age verification systems, but fundamental power imbalance remains.</p>
</div>
</div>
<!-- COMPARISON CONTENT -->
<div id="comparison" class="platform-content">
<h2>⚖️ Side-by-Side Comparison</h2>
<table class="comparison-table">
<thead>
<tr>
<th>Category</th>
<th>Meta (Facebook)</th>
<th>Roblox</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Liability Protection</strong></td>
<td>Extensive disclaimers, "as is" service, minimal responsibility for content harms</td>
<td>Platform provider shield, user-generated content defense, limited moderation guarantees</td>
<td>🤝 Tie (both terrible)</td>
</tr>
<tr>
<td><strong>Content/Data Rights</strong></td>
<td>Broad license to use all content, AI training rights, cross-platform sharing, private messages accessible</td>
<td>Creator content licenses, expanding child data collection (audio, facial, behavioral)</td>
<td>😬 Meta slightly worse (AI training of private messages)</td>
</tr>
<tr>
<td><strong>Arbitration Clauses</strong></td>
<td>Yes, mandatory arbitration</td>
<td>Yes, mandatory arbitration + class action waiver in ALL CAPS</td>
<td>🤝 Tie (both eliminate legal recourse)</td>
</tr>
<tr>
<td><strong>Term Changes</strong></td>
<td>Can change anytime, continued use = acceptance</td>
<td>Can change anytime, some changes effective immediately without notice</td>
<td>😬 Roblox slightly worse (immediate changes)</td>
</tr>
<tr>
<td><strong>Account Termination</strong></td>
<td>At discretion, minimal support for appeals, documented customer service issues</td>
<td>At discretion, automated detection possible, loss of all purchases without refund</td>
<td>😬 Roblox slightly worse (financial loss for kids)</td>
</tr>
<tr>
<td><strong>Transparency</strong></td>
<td>Unclear AI usage, vague about ad targeting specifics</td>
<td>"Clarified" expanded data collection in 2025 updates</td>
<td>🤝 Tie (both lack meaningful transparency)</td>
</tr>
<tr>
<td><strong>User Age</strong></td>
<td>13+ officially (younger users common)</td>
<td>All ages, with parent consent for minors</td>
<td>⚠️ Roblox worse (targeting younger users)</td>
</tr>
<tr>
<td><strong>Parent Liability</strong></td>
<td>Not explicitly stated</td>
<td>Parents fully responsible for all minor activities and purchases</td>
<td>😬 Roblox worse (explicit parent liability)</td>
</tr>
</tbody>
</table>
<div class="category-header">
📊 Overall Assessment
</div>
<div class="finding">
<h3>Common Patterns Across Both Platforms</h3>
<div class="key-point">
<div class="key-point-icon">🛡️</div>
<div><strong>Maximum protection for company:</strong> Extensive disclaimers, limited liability, ability to change terms unilaterally</div>
</div>
<div class="key-point">
<div class="key-point-icon">📝</div>
<div><strong>Broad content licenses:</strong> Your creative work becomes their asset for AI training, advertising, and platform development</div>
</div>
<div class="key-point">
<div class="key-point-icon">⚖️</div>
<div><strong>Eliminated legal recourse:</strong> Mandatory arbitration and class action waivers prevent collective action</div>
</div>
<div class="key-point">
<div class="key-point-icon">🔄</div>
<div><strong>Moving goalposts:</strong> Terms can change anytime; your only choice is accept or leave (losing everything)</div>
</div>
<div class="key-point">
<div class="key-point-icon">👁️</div>
<div><strong>Surveillance for profit:</strong> Both collect extensive behavioral data to sell advertising access</div>
</div>
</div>
<div class="warning">
<h3>⚠️ The Fundamental Problem</h3>
<p>These aren't "agreements" between equals. They're:</p>
<ul>
<li><strong>Adhesion contracts:</strong> Take it or leave it, no negotiation</li>
<li><strong>Deliberately incomprehensible:</strong> Written by lawyers for lawyers</li>
<li><strong>Designed not to be read:</strong> Thousands of words no one has time for</li>
<li><strong>Legally questionable:</strong> Many clauses wouldn't survive real scrutiny, but arbitration prevents that</li>
<li><strong>Ethically bankrupt:</strong> Especially for platforms targeting children</li>
</ul>
</div>
<div class="finding">
<h3>What Makes These Terms Especially Problematic</h3>
<h4>For Meta:</h4>
<ul>
<li>Expanded AI training rights without clear limits or opt-outs</li>
<li>Private messages explicitly included in data usage rights</li>
<li>"Continued use = consent" applied retroactively to years of existing content</li>
<li>Psychological manipulation (engagement optimization) is the business model</li>
</ul>
<h4>For Roblox:</h4>
<ul>
<li>Platform primarily used by children, but terms written for adults</li>
<li>Parents accept liability without informed understanding</li>
<li>Expanding data collection (facial, audio) from minors</li>
<li>Documented safety issues yet minimal platform liability</li>
<li>Real money losses (purchased content) with account termination</li>
</ul>
</div>
<div class="category-header">
💭 Discussion Questions for Workshop
</div>
<div class="finding">
<h3>For Families:</h3>
<ol>
<li>Did you know you agreed to these terms?</li>
<li>Would you have signed up if you'd understood what you were agreeing to?</li>
<li>How do you feel about your family photos training AI systems?</li>
<li>Is "continued use = consent" really meaningful consent?</li>
<li>Should companies be allowed to write one-sided contracts like this?</li>
<li>What would fair terms look like?</li>
</ol>
</div>
<div class="finding">
<h3>For Teens:</h3>
<ol>
<li>What's the most surprising thing you learned?</li>
<li>Do you trust these platforms more or less now?</li>
<li>If you could change one thing about these terms, what would it be?</li>
<li>Do you think most of your friends know what they agreed to?</li>
<li>Would you still use these platforms knowing all this?</li>
</ol>
</div>
<div class="category-header">
🛠️ What You Can Actually Do
</div>
<div class="finding">
<h3>Short-Term Actions:</h3>
<ul>
<li><strong>Be aware:</strong> At least now you know what you agreed to</li>
<li><strong>Minimize sharing:</strong> Less data shared = less data they can use</li>
<li><strong>Review settings:</strong> Use whatever privacy controls exist (limited as they are)</li>
<li><strong>Document problems:</strong> Screenshots help with appeals and reporting</li>
<li><strong>Educate others:</strong> Share what you learned</li>
</ul>
</div>
<div class="finding">
<h3>Long-Term Advocacy:</h3>
<ul>
<li><strong>Support regulation:</strong> Terms this one-sided shouldn't be enforceable</li>
<li><strong>Demand transparency:</strong> Plain language explanations should be mandatory</li>
<li><strong>Consider alternatives:</strong> Fediverse, ATProto, and other platforms have different models</li>
<li><strong>Vote with your data:</strong> Delete accounts when platforms cross lines</li>
<li><strong>Protect children:</strong> Special regulations needed for platforms targeting minors</li>
</ul>
</div>
<div class="warning">
<h3>Remember:</h3>
<p><strong>Just because it's in the Terms of Service doesn't make it legal or enforceable.</strong></p>
<ul>
<li>Consumer protection laws still apply</li>
<li>Special protections exist for minors</li>
<li>Some clauses are legally questionable (which is why they prevent you from suing)</li>
<li>Public pressure and regulation can force changes</li>
</ul>
</div>
</div>
<div style="margin-top: 50px; padding: 30px; background: #f5f5f5; border-radius: 8px;">
<h2>📚 Sources & Further Reading</h2>
<ul>
<li>Meta Terms of Service (Effective January 1, 2025): facebook.com/terms</li>
<li>Roblox Terms of Use (Effective September 17, 2025)</li>
<li>Bloomberg's 2024 Roblox investigation</li>
<li>Analysis of 2025 Meta ToS updates from tech journalists and legal experts</li>
<li>Consumer advocacy organizations (EFF, Common Sense Media, etc.)</li>
</ul>
<p style="margin-top: 20px; font-style: italic;">
<strong>Workshop Note:</strong> This analysis is meant for education and critical thinking, not legal advice.
Actual Terms of Service are available on each platform's website. Encourage participants to read (or attempt to read) the real documents.
</p>
</div>
</div>
<script>
function showPlatform(platform) {
// Hide all content
document.querySelectorAll('.platform-content').forEach(content => {
content.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected content
document.getElementById(platform).classList.add('active');
event.target.closest('.tab').classList.add('active');
}
</script>
</body>
</html>