514 lines
16 KiB
HTML
514 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Bitfielder</title>
|
|
<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">
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
background: #000;
|
|
color: #fff;
|
|
font-family: monospace;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#canvas {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
image-rendering: pixelated;
|
|
image-rendering: -moz-crisp-edges;
|
|
image-rendering: crisp-edges;
|
|
}
|
|
|
|
#topbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 40px;
|
|
background: rgba(0, 0, 0, 0.8);
|
|
border-bottom: 1px solid #333;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 20px;
|
|
z-index: 100;
|
|
}
|
|
|
|
#topbar .title {
|
|
color: #fff;
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
margin-right: 20px;
|
|
}
|
|
|
|
#topbar .controls {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
#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: monospace;
|
|
font-size: 12px;
|
|
}
|
|
|
|
#topbar button:hover {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
#editor-panel {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 140px;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
display: flex;
|
|
z-index: 100;
|
|
backdrop-filter: blur(5px);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
#editor-panel.minimal {
|
|
height: 50px;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
right: 20px;
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
#editor {
|
|
width: 100%;
|
|
background: rgba(0, 0, 0, 0.3);
|
|
border: none;
|
|
color: #fff;
|
|
font-family: monospace;
|
|
font-size: 16px;
|
|
padding: 15px;
|
|
resize: none;
|
|
outline: none;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
#editor.minimal {
|
|
padding: 12px 15px;
|
|
font-size: 14px;
|
|
background: rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
#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: 500px;
|
|
display: none;
|
|
}
|
|
|
|
#help-popup h3 {
|
|
margin-bottom: 20px;
|
|
color: #fff;
|
|
font-size: 18px;
|
|
}
|
|
|
|
#help-popup .help-section {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
#help-popup .help-section h4 {
|
|
color: #ccc;
|
|
margin-bottom: 10px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
#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, 0.7);
|
|
border: 1px solid #555;
|
|
color: #fff;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-family: 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: 0;
|
|
right: -300px;
|
|
width: 300px;
|
|
height: 100vh;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
|
z-index: 90;
|
|
transition: right 0.3s ease;
|
|
backdrop-filter: blur(10px);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
|
|
#shader-library-trigger {
|
|
position: fixed;
|
|
top: 0;
|
|
right: 0;
|
|
width: 20px;
|
|
height: 100vh;
|
|
z-index: 91;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#shader-library-trigger:hover + #shader-library,
|
|
#shader-library:hover {
|
|
right: 0;
|
|
}
|
|
|
|
#shader-library.open {
|
|
right: 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: 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: 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: 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: 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, 0.3);
|
|
color: #ccc;
|
|
font-family: 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;
|
|
}
|
|
</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">
|
|
<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>
|
|
</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;">
|
|
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>
|
|
</select>
|
|
</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="share-btn">Share</button>
|
|
<button id="export-png-btn">Export PNG</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="editor-panel">
|
|
<textarea id="editor" placeholder="Enter shader code... (x, y, t, i, mouseX, mouseY)" spellcheck="false">x^y</textarea>
|
|
</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">×</button>
|
|
<h3>Bitfielder Help</h3>
|
|
|
|
<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>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>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>
|
|
</div>
|
|
|
|
<div class="help-section">
|
|
<h4>Operators</h4>
|
|
<p><strong>^ & |</strong> - XOR, AND, OR</p>
|
|
<p><strong><< >></strong> - Bit shift left/right</p>
|
|
<p><strong>+ - * / %</strong> - Math operations</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 id="performance-warning">
|
|
Performance warning: Shader taking too long to render!
|
|
</div>
|
|
|
|
<script type="module" src="/src/main.ts"></script>
|
|
</body>
|
|
</html>
|