Files
bitfielder/index.html
2025-07-06 01:14:43 +02:00

1073 lines
37 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Bitfielder</title>
<!-- PWA Meta Tags -->
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#000000">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Bitfielder">
<link rel="apple-touch-icon" href="/icon-192.png">
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512.png">
<meta name="description" content="Bitfielder is a live bitfield shader editor for creating visual patterns using bitwise operations.">
<meta name="author" content="BuboBubo">
<meta name="keywords" content="shader, bitfield, visual, patterns, programming, interactive, editor">
<meta property="og:title" content="Bitfielder - Bitfield Shader App">
<meta property="og:description" content="Interactive bitfield shader editor for creating visual patterns using bitwise operations">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Bitfielder - Bitfield Shader App">
<meta name="twitter:description" content="Interactive bitfield shader editor for creating visual patterns using bitwise operations">
<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=IBM+Plex+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--ui-opacity: 0.3;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
color: #fff;
font-family: 'IBM Plex Mono', monospace;
overflow: hidden;
touch-action: manipulation; /* Allow pan and zoom but disable double-tap zoom */
}
#canvas {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
touch-action: none; /* Disable all touch gestures on canvas for shader interaction */
pointer-events: auto; /* Allow canvas interactions */
}
#topbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 40px;
background: rgba(0, 0, 0, var(--ui-opacity));
border-bottom: 1px solid #333;
display: flex;
align-items: center;
padding: 0 20px;
z-index: 100;
pointer-events: auto; /* Ensure topbar can be clicked */
touch-action: manipulation; /* Allow normal touch interactions */
}
#topbar .title {
color: #fff;
font-size: 14px;
font-weight: bold;
margin-right: 20px;
}
#topbar .controls {
display: flex;
gap: 10px;
margin-left: auto;
align-items: center;
}
#topbar .controls-desktop {
display: flex;
gap: 10px;
align-items: center;
}
#topbar .controls-mobile {
display: none;
gap: 8px;
align-items: center;
margin-left: auto;
}
#hamburger-menu {
display: none;
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
width: 36px;
height: 36px;
padding: 0;
border-radius: 4px;
cursor: pointer;
align-items: center;
justify-content: center;
}
#hamburger-menu:hover {
background: rgba(255, 255, 255, 0.2);
}
#hamburger-menu svg {
width: 18px;
height: 18px;
}
#mobile-menu {
position: fixed;
top: 40px;
right: -320px;
width: 320px;
max-width: 80vw;
height: calc(100vh - 40px);
background: rgba(0, 0, 0, var(--ui-opacity));
backdrop-filter: blur(3px);
border-left: 1px solid rgba(255, 255, 255, 0.1);
z-index: 150;
transition: right 0.3s ease;
overflow-y: auto;
padding: 20px;
pointer-events: auto; /* Ensure mobile menu can be clicked */
touch-action: manipulation; /* Allow normal touch interactions */
}
#mobile-menu.open {
right: 0;
}
#mobile-menu h3 {
color: #fff;
font-size: 16px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.mobile-menu-section {
margin-bottom: 20px;
}
.mobile-menu-item {
margin-bottom: 15px;
}
.mobile-menu-item label {
display: block;
color: #ccc;
font-size: 12px;
margin-bottom: 5px;
}
.mobile-menu-item select,
.mobile-menu-item input[type="range"] {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 8px;
border-radius: 4px;
font-size: 14px;
}
.mobile-menu-buttons {
display: flex;
flex-direction: column;
gap: 10px;
}
.mobile-menu-buttons button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 12px;
border-radius: 4px;
cursor: pointer;
font-family: 'IBM Plex Mono', monospace;
font-size: 14px;
text-align: left;
display: flex;
align-items: center;
gap: 10px;
}
.mobile-menu-buttons button:hover {
background: rgba(255, 255, 255, 0.2);
}
#mobile-menu-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 149;
pointer-events: none; /* Don't block clicks when hidden */
}
#mobile-menu-overlay.open {
display: block;
pointer-events: auto; /* Only block clicks when visible */
}
#topbar button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
}
#topbar button:hover {
background: rgba(255, 255, 255, 0.2);
}
.icon-button {
width: 36px;
height: 36px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
}
.icon-button svg {
width: 18px;
height: 18px;
}
/* Lucide icon styles */
[data-lucide] {
display: inline-block;
vertical-align: middle;
}
button svg {
pointer-events: none;
}
/* Ensure all button contents don't intercept clicks */
button *, button svg, button [data-lucide] {
pointer-events: none !important;
}
#editor-panel {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 140px;
display: flex;
align-items: stretch;
gap: 10px;
padding: 10px;
z-index: 100;
transition: all 0.3s ease;
pointer-events: auto; /* Ensure editor panel can be clicked */
touch-action: manipulation; /* Allow normal touch interactions */
}
#editor-panel.minimal {
height: 50px;
bottom: 20px;
left: 20px;
right: 20px;
padding: 5px;
}
#editor {
flex: 1;
background: rgba(0, 0, 0, var(--ui-opacity));
backdrop-filter: blur(2px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 4px;
color: #fff;
font-family: 'IBM Plex Mono', monospace;
font-size: 16px;
padding: 15px;
resize: none;
outline: none;
transition: all 0.3s ease;
touch-action: manipulation; /* Allow normal touch interactions for text editing */
}
#eval-btn {
background: rgba(0, 0, 0, var(--ui-opacity));
backdrop-filter: blur(2px);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
padding: 20px 30px;
font-family: 'IBM Plex Mono', monospace;
font-size: 16px;
font-weight: bold;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s ease;
align-self: stretch;
}
#eval-btn:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.4);
}
#eval-btn:active {
transform: scale(0.95);
}
#editor.minimal {
padding: 12px 15px;
font-size: 14px;
}
#eval-btn.minimal {
padding: 10px 20px;
font-size: 14px;
}
#help-popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.95);
border: 1px solid #555;
border-radius: 8px;
padding: 30px;
z-index: 1000;
max-width: 90vw;
width: 800px;
max-height: 80vh;
overflow-y: auto;
display: none;
pointer-events: auto; /* Ensure help popup can be clicked */
touch-action: manipulation; /* Allow normal touch interactions */
}
#help-popup h3 {
margin-bottom: 20px;
color: #fff;
font-size: 18px;
text-align: center;
}
.help-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin-top: 20px;
}
#help-popup .help-section {
margin-bottom: 0;
}
#help-popup .help-section h4 {
color: #ccc;
margin-bottom: 10px;
font-size: 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
padding-bottom: 5px;
}
#help-popup .help-section p {
color: #999;
font-size: 12px;
line-height: 1.5;
margin-bottom: 8px;
}
#help-popup .close-btn {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
color: #999;
font-size: 20px;
cursor: pointer;
}
.hidden {
display: none !important;
}
#show-ui-btn {
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, var(--ui-opacity));
border: 1px solid #555;
color: #fff;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
z-index: 1000;
display: none;
}
#show-ui-btn:hover {
background: rgba(0, 0, 0, 0.9);
}
#shader-library {
position: fixed;
top: 40px;
left: -300px;
width: 300px;
height: calc(100vh - 40px);
background: rgba(0, 0, 0, calc(var(--ui-opacity) + 0.1));
border-right: 1px solid rgba(255, 255, 255, 0.1);
z-index: 90;
transition: left 0.3s ease;
backdrop-filter: blur(3px);
overflow-y: auto;
pointer-events: auto; /* Ensure shader library can be clicked */
touch-action: manipulation; /* Allow normal touch interactions */
}
#shader-library-trigger {
position: fixed;
top: 40px;
left: 0;
width: 20px;
height: calc(100vh - 40px);
z-index: 91;
cursor: pointer;
}
#shader-library-trigger:hover + #shader-library,
#shader-library:hover {
left: 0;
}
#shader-library.open {
left: 0;
}
.library-header {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.library-header h3 {
margin: 0 0 15px 0;
color: #fff;
font-size: 16px;
}
.save-shader {
display: flex;
gap: 8px;
margin-bottom: 15px;
}
.search-shader {
margin-bottom: 10px;
}
.search-shader input {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 6px 8px;
border-radius: 4px;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
}
.search-shader input::placeholder {
color: #999;
}
.save-shader input {
flex: 1;
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 6px 8px;
border-radius: 4px;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
}
.save-shader button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid #555;
color: #fff;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
}
.save-shader button:hover {
background: rgba(255, 255, 255, 0.2);
}
.shader-list {
padding: 0 20px 20px 20px;
}
.shader-item {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px;
margin-bottom: 8px;
overflow: hidden;
}
.shader-item-header {
padding: 10px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.shader-item-header:hover {
background: rgba(255, 255, 255, 0.1);
}
.shader-name {
color: #fff;
font-size: 12px;
font-weight: bold;
}
.shader-actions {
display: flex;
gap: 4px;
}
.shader-action {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #ccc;
cursor: pointer;
font-size: 10px;
padding: 4px 6px;
border-radius: 3px;
transition: all 0.2s ease;
font-family: 'IBM Plex Mono', monospace;
}
.shader-action:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
transform: scale(1.05);
}
.shader-action.rename {
background: rgba(52, 152, 219, 0.3);
border-color: rgba(52, 152, 219, 0.5);
}
.shader-action.rename:hover {
background: rgba(52, 152, 219, 0.5);
}
.shader-action.delete {
background: rgba(231, 76, 60, 0.3);
border-color: rgba(231, 76, 60, 0.5);
}
.shader-action.delete:hover {
background: rgba(231, 76, 60, 0.5);
}
.shader-code {
padding: 8px 10px;
background: rgba(0, 0, 0, var(--ui-opacity));
color: #ccc;
font-family: 'IBM Plex Mono', monospace;
font-size: 11px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
word-break: break-all;
}
#performance-warning {
position: fixed;
top: 50px;
right: 20px;
background: rgba(255, 0, 0, 0.8);
color: #fff;
padding: 10px 15px;
border-radius: 4px;
font-size: 12px;
z-index: 1001;
display: none;
}
/* Responsive Design */
@media (max-width: 768px) {
#topbar .controls {
margin-left: auto;
}
#topbar .controls-desktop {
display: none;
}
#topbar .controls-mobile {
display: flex;
}
#hamburger-menu {
display: flex;
}
#topbar {
height: 40px;
padding: 0 10px;
}
#topbar .title {
margin-right: auto;
}
#topbar .controls {
flex-wrap: wrap;
gap: 5px;
margin-left: 0;
}
#topbar button {
padding: 4px 8px;
font-size: 11px;
}
#topbar label {
font-size: 11px !important;
margin-right: 5px !important;
}
#topbar select {
padding: 2px !important;
font-size: 11px !important;
}
#help-popup {
width: 95vw;
max-width: 95vw;
max-height: 90vh;
padding: 20px;
}
.help-content {
grid-template-columns: 1fr;
gap: 20px;
}
#editor-panel {
height: 120px;
}
#editor {
font-size: 14px;
padding: 10px;
}
#shader-library {
width: 100%;
left: -100%;
top: 40px;
height: calc(100vh - 40px);
}
#shader-library-trigger {
display: none;
}
}
@media (max-width: 480px) {
#topbar {
padding: 5px;
}
#topbar .title {
font-size: 12px;
}
#topbar button {
padding: 3px 6px;
font-size: 10px;
}
#topbar label {
font-size: 10px !important;
}
#topbar select {
font-size: 10px !important;
}
#help-popup {
padding: 15px;
}
#help-popup h3 {
font-size: 16px;
}
#help-popup .help-section h4 {
font-size: 13px;
}
#help-popup .help-section p {
font-size: 11px;
}
#editor-panel {
height: 100px;
}
#editor {
font-size: 12px;
padding: 8px;
}
}
@media (min-width: 1200px) {
.help-content {
grid-template-columns: repeat(3, 1fr);
}
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<button id="show-ui-btn">Show UI</button>
<div id="topbar">
<div class="title">Bitfielder</div>
<div class="controls">
<div class="controls-desktop">
<label style="color: #ccc; font-size: 12px; margin-right: 10px;">
Resolution:
<select id="resolution-select" style="background: rgba(255,255,255,0.1); border: 1px solid #555; color: #fff; padding: 4px; border-radius: 4px;">
<option value="1">Full (1x)</option>
<option value="2">Half (2x)</option>
<option value="4">Quarter (4x)</option>
<option value="8">Eighth (8x)</option>
<option value="16">Sixteenth (16x)</option>
<option value="32">Thirty-second (32x)</option>
</select>
</label>
<label style="color: #ccc; font-size: 12px; margin-right: 10px;">
FPS:
<select id="fps-select" style="background: rgba(255,255,255,0.1); border: 1px solid #555; color: #fff; padding: 4px; border-radius: 4px;">
<option value="15">15 FPS</option>
<option value="30" selected>30 FPS</option>
<option value="60">60 FPS</option>
</select>
</label>
<label style="color: #ccc; font-size: 12px; margin-right: 10px;">
Value Mode:
<select id="value-mode-select" style="background: rgba(255,255,255,0.1); border: 1px solid #555; color: #fff; padding: 4px; border-radius: 4px;">
<option value="integer" selected>Integer (0-255)</option>
<option value="float">Float (0.0-1.0)</option>
<option value="polar">Polar (angle-based)</option>
<option value="distance">Distance (radial)</option>
<option value="wave">Wave (ripple)</option>
</select>
</label>
<label style="color: #ccc; font-size: 12px; margin-right: 10px;">
Render Mode:
<select id="render-mode-select" style="background: rgba(255,255,255,0.1); border: 1px solid #555; color: #fff; padding: 4px; border-radius: 4px;">
<option value="classic" selected>Classic</option>
<option value="grayscale">Grayscale</option>
<option value="red">Red Channel</option>
<option value="green">Green Channel</option>
<option value="blue">Blue Channel</option>
<option value="rgb">RGB Split</option>
<option value="hsv">HSV</option>
<option value="rainbow">Rainbow</option>
<option value="thermal">Thermal</option>
<option value="neon">Neon</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="vaporwave">Vaporwave</option>
<option value="dithered">Dithered</option>
<option value="palette">Palette</option>
</select>
</label>
<label style="color: #ccc; font-size: 12px; margin-right: 10px;">
UI Opacity:
<input type="range" id="opacity-slider" min="10" max="100" value="30" style="width: 80px; vertical-align: middle;">
<span id="opacity-value" style="font-size: 11px;">30%</span>
</label>
<button id="help-btn">?</button>
<button id="fullscreen-btn">Fullscreen</button>
<button id="hide-ui-btn">Hide UI</button>
<button id="random-btn">Random</button>
<button id="audio-btn">Enable Audio</button>
<button id="share-btn">Share</button>
<button id="export-png-btn">Export PNG</button>
</div>
<div class="controls-mobile">
<button id="library-btn-mobile" class="icon-button" aria-label="Shader Library"></button>
<button id="random-btn-mobile" class="icon-button" aria-label="Random"></button>
<button id="hide-ui-btn-mobile" class="icon-button" aria-label="Hide UI"></button>
<button id="hamburger-menu" class="icon-button" aria-label="Menu"></button>
</div>
</div>
</div>
<div id="mobile-menu-overlay"></div>
<div id="mobile-menu">
<h3>Settings</h3>
<div class="mobile-menu-section">
<div class="mobile-menu-item">
<label>Resolution</label>
<select id="resolution-select-mobile">
<option value="1">Full (1x)</option>
<option value="2">Half (2x)</option>
<option value="4">Quarter (4x)</option>
<option value="8">Eighth (8x)</option>
<option value="16">Sixteenth (16x)</option>
<option value="32">Thirty-second (32x)</option>
</select>
</div>
<div class="mobile-menu-item">
<label>FPS</label>
<select id="fps-select-mobile">
<option value="15">15 FPS</option>
<option value="30" selected>30 FPS</option>
<option value="60">60 FPS</option>
</select>
</div>
<div class="mobile-menu-item">
<label>Value Mode</label>
<select id="value-mode-select-mobile">
<option value="integer" selected>Integer (0-255)</option>
<option value="float">Float (0.0-1.0)</option>
<option value="polar">Polar (angle-based)</option>
<option value="distance">Distance (radial)</option>
<option value="wave">Wave (ripple)</option>
</select>
</div>
<div class="mobile-menu-item">
<label>Render Mode</label>
<select id="render-mode-select-mobile">
<option value="classic" selected>Classic</option>
<option value="grayscale">Grayscale</option>
<option value="red">Red Channel</option>
<option value="green">Green Channel</option>
<option value="blue">Blue Channel</option>
<option value="rgb">RGB Split</option>
<option value="hsv">HSV</option>
<option value="rainbow">Rainbow</option>
<option value="thermal">Thermal</option>
<option value="neon">Neon</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="vaporwave">Vaporwave</option>
<option value="dithered">Dithered</option>
<option value="palette">Palette</option>
</select>
</div>
<div class="mobile-menu-item">
<label>UI Opacity: <span id="opacity-value-mobile">30%</span></label>
<input type="range" id="opacity-slider-mobile" min="10" max="100" value="30">
</div>
</div>
<div class="mobile-menu-section">
<div class="mobile-menu-buttons">
<button id="help-btn-mobile"><span class="icon"></span> Help</button>
<button id="fullscreen-btn-mobile"><span class="icon"></span> Fullscreen</button>
<button id="audio-btn-mobile"><span class="icon"></span> Enable Audio</button>
<button id="share-btn-mobile"><span class="icon"></span> Share</button>
<button id="export-png-btn-mobile"><span class="icon"></span> Export PNG</button>
</div>
</div>
</div>
<div id="editor-panel">
<textarea id="editor" placeholder="Enter shader code... (x, y, t, i, mouseX, mouseY, mousePressed, touchCount, accelX, audioLevel, bassLevel...)" spellcheck="false">x^y</textarea>
<button id="eval-btn">Eval</button>
</div>
<div id="shader-library-trigger"></div>
<div id="shader-library">
<div class="library-header">
<h3>Shader Library</h3>
<div class="save-shader">
<input type="text" id="shader-name-input" placeholder="Shader name..." maxlength="30">
<button id="save-shader-btn">Save</button>
</div>
<div class="search-shader">
<input type="text" id="shader-search-input" placeholder="Search shaders...">
</div>
</div>
<div class="shader-list" id="shader-list">
<!-- Saved shaders will appear here -->
</div>
</div>
<div id="help-popup">
<button class="close-btn">&times;</button>
<h3>Bitfielder Help</h3>
<div class="help-content">
<div class="help-section">
<h4>Keyboard Shortcuts</h4>
<p><strong>Ctrl+Enter</strong> - Execute shader code</p>
<p><strong>F11</strong> - Toggle fullscreen</p>
<p><strong>H</strong> - Hide/show UI</p>
<p><strong>R</strong> - Generate random shader</p>
<p><strong>S</strong> - Share current shader (copy URL)</p>
<p><strong>?</strong> - Show this help</p>
</div>
<div class="help-section">
<h4>Variables</h4>
<p><strong>x, y</strong> - Pixel coordinates</p>
<p><strong>t</strong> - Time (enables animation)</p>
<p><strong>i</strong> - Pixel index</p>
<p><strong>mouseX, mouseY</strong> - Mouse position (0.0 to 1.0)</p>
<p><strong>mousePressed</strong> - Mouse button down (true/false)</p>
<p><strong>mouseVX, mouseVY</strong> - Mouse velocity</p>
<p><strong>mouseClickTime</strong> - Time since last click (ms)</p>
</div>
<div class="help-section">
<h4>Touch & Gestures</h4>
<p><strong>touchCount</strong> - Number of active touches</p>
<p><strong>touch0X, touch0Y</strong> - Primary touch position</p>
<p><strong>touch1X, touch1Y</strong> - Secondary touch position</p>
<p><strong>pinchScale</strong> - Pinch zoom scale factor</p>
<p><strong>pinchRotation</strong> - Pinch rotation angle</p>
</div>
<div class="help-section">
<h4>Device Motion</h4>
<p><strong>accelX, accelY, accelZ</strong> - Accelerometer data</p>
<p><strong>gyroX, gyroY, gyroZ</strong> - Gyroscope rotation rates</p>
</div>
<div class="help-section">
<h4>Audio Reactive</h4>
<p><strong>audioLevel</strong> - Overall audio volume (0.0-1.0)</p>
<p><strong>bassLevel</strong> - Low frequencies (0.0-1.0)</p>
<p><strong>midLevel</strong> - Mid frequencies (0.0-1.0)</p>
<p><strong>trebleLevel</strong> - High frequencies (0.0-1.0)</p>
<p>Click "Enable Audio" to activate microphone</p>
</div>
<div class="help-section">
<h4>Operators</h4>
<p><strong>^ & |</strong> - XOR, AND, OR</p>
<p><strong>&lt;&lt; &gt;&gt;</strong> - Bit shift left/right</p>
<p><strong>+ - * / %</strong> - Math operations</p>
<p><strong>== != &lt; &gt;</strong> - Comparisons (return 0/1)</p>
<p><strong>? :</strong> - Ternary operator (condition ? true : false)</p>
<p><strong>~ **</strong> - Bitwise NOT, exponentiation</p>
</div>
<div class="help-section">
<h4>Math Functions</h4>
<p><strong>sin, cos, tan</strong> - Trigonometric functions</p>
<p><strong>abs, sqrt, pow</strong> - Absolute, square root, power</p>
<p><strong>floor, ceil, round</strong> - Rounding functions</p>
<p><strong>min, max</strong> - Minimum and maximum</p>
<p><strong>random</strong> - Random number 0-1</p>
<p><strong>log, exp</strong> - Natural logarithm, exponential</p>
<p><strong>PI, E</strong> - Math constants</p>
<p>Use without Math. prefix: <code>sin(x)</code> not <code>Math.sin(x)</code></p>
</div>
<div class="help-section">
<h4>Value Modes</h4>
<p><strong>Integer (0-255):</strong> Traditional mode for large values</p>
<p><strong>Float (0.0-1.0):</strong> Bitfield shader mode, inverts and clamps values</p>
<p><strong>Polar (angle-based):</strong> Spiral patterns combining angle and radius</p>
<p><strong>Distance (radial):</strong> Concentric wave rings with variable frequency</p>
<p><strong>Wave (ripple):</strong> Multi-source interference with amplitude falloff</p>
<p>Each mode transforms your expression differently!</p>
</div>
<div class="help-section">
<h4>Advanced Features</h4>
<p><strong>Array indexing:</strong> <code>[1,2,4,8][floor(t%4)]</code></p>
<p><strong>Complex expressions:</strong> <code>x&gt;y ? sin(x) : cos(y)</code></p>
<p><strong>Nested functions:</strong> <code>pow(sin(x), abs(y-x))</code></p>
<p><strong>Logical operators:</strong> <code>x&amp;&amp;y</code>, <code>x||y</code></p>
<p>No character or length limits - use any JavaScript!</p>
</div>
<div class="help-section">
<h4>Shader Library</h4>
<p>Hover over the <strong>right edge</strong> of the screen to access the shader library</p>
<p>Save shaders with custom names and search through them</p>
<p>Use <strong>edit</strong> to rename, <strong>del</strong> to delete</p>
</div>
<div class="help-section">
<h4>Render Modes</h4>
<p><strong>Classic</strong> - Original colorful mode</p>
<p><strong>Grayscale</strong> - Black and white</p>
<p><strong>Red/Green/Blue</strong> - Single color channels</p>
<p><strong>HSV</strong> - Hue-based coloring</p>
<p><strong>Rainbow</strong> - Spectrum coloring</p>
</div>
<div class="help-section">
<h4>Export</h4>
<p><strong>Export PNG</strong> - Save current frame as image</p>
</div>
</div>
<div class="help-section" style="grid-column: 1 / -1; margin-top: 20px; text-align: center; padding-top: 20px; border-bottom: none;">
<h4>About</h4>
<p><strong>Bitfielder</strong> - Interactive bitfield shader editor</p>
<p>Created by <strong>BuboBubo</strong> (Raphaël Forment)</p>
<p>Website: <a href="https://raphaelforment.fr" target="_blank" style="color: #4A9EFF;">raphaelforment.fr</a></p>
<p>Source: <a href="https://git.raphaelforment.fr" target="_blank" style="color: #4A9EFF;">git.raphaelforment.fr</a></p>
<p>License: <strong>AGPL 3.0</strong></p>
</div>
</div>
<div id="performance-warning">
Performance warning: Shader taking too long to render!
</div>
<script type="module" src="/src/main.ts"></script>
<!-- PWA Service Worker Registration -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered: ', registration);
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
</script>
</body>
</html>