AquaMotion 1.3.1
by Sharxxy
HTML
<!-- AquaMotion - single-file starter -->
<div id="topbar">
<img id="logo" src="https://via.placeholder.com/140x35?text=AquaMotion" alt="AquaMotion">
<div class="tools-row">
<div class="tool-group">
<button data-tool="pencil" class="tool active">✏️ Pencil</button>
<button data-tool="brush" class="tool">🖌️ Brush</button>
<button data-tool="squarebrush" class="tool">⬜ SqBrush</button>
<button data-tool="splatter" class="tool">💦 Splatter</button>
<button data-tool="eraser" class="tool">🩹 Eraser</button>
<button data-tool="line" class="tool">— Line</button>
<button data-tool="rect" class="tool">⬛ Rect</button>
<button data-tool="circle" class="tool">⭕ Circle</button>
<button data-tool="bucket" class="tool">🪣 Bucket</button>
<button data-tool="text" class="tool">🔤 Text</button>
</div>
<div class="tool-group">
<label>Color <input id="colorPicker" type="color" value="#00bfff"></label>
<label>Size <input id="brushSize" type="range" min="1" max="80" value="6"></label>
<label>Brush <select id="brushStyle">
<option value="round">Round</option>
<option value="square">Square</option>
<option value="jitter">Jitter</option>
<option value="pressure">Pressure</option>
</select></label>
<button id="pathToggle" class="tool">🔁 Path Cursor</button>
</div>
<div class="tool-group">
<button id="undo">↩️ Undo</button>
<button id="redo">↪️ Redo</button>
<button id="playPause">▶️ Play</button>
<label>FPS <input id="fps" type="number" min="1" max="60" value="12"></label>
<label>Zoom <button id="zoomOut">−</button><span id="zoomVal">100%</span><button id="zoomIn">+</button></label>
<label>Canvas W <input id="canvasWidth" type="number" min="100" max="3000" value="800"></label>
<label>H <input id="canvasHeight" type="number" min="100" max="2000" value="600"></label>
...
CSS
/* AquaMotion — watery vibes */
*{box-sizing:border-box}
body{margin:0;font-family:Inter,Segoe UI,Arial;background:linear-gradient(180deg,#041429,#06283a);color:#eaf6ff;overflow:hidden}
#topbar{height:64px;padding:8px 12px;border-bottom:2px solid rgba(79,195,247,0.12);display:flex;align-items:center;gap:12px}
#logo{height:40px;margin-right:8px}
.tools-row{display:flex;flex:1;align-items:center;gap:10px;flex-wrap:wrap}
.tool-group{display:flex;gap:6px;align-items:center}
.tool{background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.03);padding:6px 8px;border-radius:8px;color:#cfefff;cursor:pointer}
.tool.active{background:linear-gradient(90deg,#2bb7ff55,#46e6ff44);box-shadow:0 6px 18px rgba(34,194,255,0.12);color:#012433}
.tool.right{margin-left:auto}
label{font-size:13px;color:#cfe;display:flex;align-items:center;gap:6px}
input[type=color],input[type=range],select,input[type=number]{background:rgba(255,255,255,0.04);border:none;color:#eaffff;padding:4px;border-radius:6px}
#workspace{display:flex;flex-direction:column;height:calc(100vh - 64px)}
#canvasWrap{flex:1;display:flex;justify-content:center;align-items:center;padding:14px}
#canvas{background:radial-gradient(circle at 20% 20%,#072b4a,#031825);border:3px solid rgba(79,195,247,0.18);box-shadow:0 10px 40px rgba(5,22,38,0.6);max-width:100%;max-height:100%;}
#bottomBar{height:140px;display:flex;gap:12px;align-items:flex-start;padding:8px;border-top:2px solid rgba(79,195,247,0.06);background:linear-gradient(180deg,rgba(3,26,46,0.6),rgba(2,18,30,0.9))}
#framesStrip{flex:1;display:flex;flex-direction:column;gap:8px}
#framesList.strip{display:flex;gap:8px;align-items:center;overflow-x:auto;padding:6px}
.frame{min-width:96px;height:72px;background:linear-gradient(180deg,rgba(255,255,255,0.03),rgba(255,255,255,0.01));border-radius:8px;border:1px solid...
JavaScript
// AquaMotion — big rewrite with fixes & extras
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let state = {
tool: 'pencil',
color: '#00bfff',
size: 6,
brushStyle: 'round',
pathCursor: false,
zoom: 1,
pan: {x:0,y:0},
draggingPan: false,
spaceDown: false
};
// data model: frames -> layers -> strokes, audios
let frames = [{ layers: [ { name: 'Layer 1', visible: true, opacity:1, strokes:[], bgColor: null } ], audios: [] }];
let currentFrame = 0;
let currentLayerIndex = 0;
// undo/redo per frame+layer: stack of actions
let undoStack = [];
let redoStack = [];
// playback
let fps = 12;
let playing = false;
let playInterval = null;
// UI refs
const colorPicker = document.getElementById('colorPicker');
const brushSizeInput = document.getElementById('brushSize');
const fpsInput = document.getElementById('fps');
const framesListEl = document.getElementById('framesList');
const addFrameBtn = document.getElementById('addFrame');
const dupFrameBtn = document.getElementById('addFrame'); // reused
const delFrameBtn = document.getElementById('delFrame'); // not in DOM (we rely on frame context menu)
const layersListEl = document.getElementById('layersList');
const addLayerBtn = document.getElementById('addLayer');
const delLayerBtn = document.getElementById('delLayer');
const undoBtn = document.getElementById('undo');
const redoBtn = document.getElementById('redo');
const playBtn = document.getElementById('playPause');
const zoomInBtn = document.getElementById('zoomIn');
const zoomOutBtn = document.getElementById('zoomOut');
const zoomVal = document.getElementById('zoomVal');
const canvasWidthInput = document.getElementById('canvasWidth');
const canvasHeightInput = document.getElementById('canvasHeight');
const applySizeBtn = document.getElementById('applySize');
const audioFileInput = document.getElementById('audioFile');
const audioStartInput =...