⌖
▭
◯
╱
✎
⬡
∪
−
∩
🗑
⧉
⬇
Select 0, 0 0 shapes 100%

VectorForge

Draw once. Live forever.

Fill & Stroke
100%
2
Animation Preset
Static
Pulse
Breathe
Orbit
Morph
Glitch
Trail
Chaos
1x
1
Transform
0°
1x
Layers
'; } svgContent += ''; return svgContent; } function generateAnimationEngine(shapes) { let code = ''; code += ' const shapes = ' + JSON.stringify(shapes.map(s => ({ id: s.id, type: s.type, anim: s.anim, speed: s.animSpeed || 1, intensity: s.animIntensity || 1, baseAttrs: {...s.attrs} }))) + '; '; code += ` let time = 0; function animate() { time += 0.016; for(const s of shapes) { const el = document.getElementById(s.id); if(!el) continue; const sp = s.speed; const int = s.intensity; const t = time * sp; switch(s.anim) { case 'pulse': { const scale = 1 + Math.sin(t * 3) * 0.1 * int; const opacity = 0.6 + Math.sin(t * 3) * 0.4 * int; el.style.transformOrigin = 'center'; el.style.transform = 'scale(' + scale + ')'; el.setAttribute('fill-opacity', opacity); break; } case 'breathe': { const swell = Math.sin(t * 2) * 5 * int; if(s.baseAttrs.rx !== undefined) { el.setAttribute('rx', (s.baseAttrs.rx || 50) + swell); el.setAttribute('ry', (s.baseAttrs.ry || 50) + swell); } else if(s.baseAttrs.width !== undefined) { el.setAttribute('width', (s.baseAttrs.width || 100) + swell); el.setAttribute('height', (s.baseAttrs.height || 100) + swell); } break; } case 'orbit': { const cx = s.baseAttrs.cx || s.baseAttrs.x || 400; const cy = s.baseAttrs.cy || s.baseAttrs.y || 300; const radius = 50 * int; const x = cx + Math.cos(t) * radius; const y = cy + Math.sin(t) * radius; if(s.baseAttrs.cx !== undefined) { el.setAttribute('cx', x); el.setAttribute('cy', y); } else { el.setAttribute('x', x - (s.baseAttrs.width||0)/2); el.setAttribute('y', y - (s.baseAttrs.height||0)/2); } break; } case 'morph': { const hue = (t * 30) % 360; el.setAttribute('fill', 'hsl(' + hue + ', 70%, 50%)'); el.setAttribute('stroke', 'hsl(' + (hue + 180) + ', 70%, 50%)'); break; } case 'glitch': { if(Math.random() < 0.05 * int) { const dx = (Math.random() - 0.5) * 20 * int; const dy = (Math.random() - 0.5) * 20 * int; el.setAttribute('transform', 'translate(' + dx + ',' + dy + ')'); } else { el.setAttribute('transform', ''); } break; } case 'trail': { const opacity = 0.3 + Math.sin(t * 5) * 0.3 * int; el.setAttribute('stroke-opacity', opacity); el.setAttribute('stroke-width', (s.baseAttrs['stroke-width'] || 2) + Math.sin(t * 5) * 3 * int); break; } case 'chaos': { const noise = Math.sin(t * 7) * Math.cos(t * 3) * 10 * int; if(s.baseAttrs.cx !== undefined) { el.setAttribute('cx', (s.baseAttrs.cx || 0) + noise); } else if(s.baseAttrs.x !== undefined) { el.setAttribute('x', (s.baseAttrs.x || 0) + noise); } break; } } } requestAnimationFrame(animate); } animate(); `; return code; } function showExport() { const code = generateLivingSVG(); document.getElementById('codePreview').textContent = code.substring(0, 2000) + (code.length > 2000 ? ' ... (' + (code.length - 2000) + ' more chars)' : ''); document.getElementById('exportModal').classList.add('show'); } function hideExport() { document.getElementById('exportModal').classList.remove('show'); } function downloadSVG() { const code = generateLivingSVG(); const blob = new Blob([code], {type: 'image/svg+xml'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'living_art_' + Date.now() + '.svg'; a.click(); URL.revokeObjectURL(url); } // ==================== KEYBOARD SHORTCUTS ==================== document.addEventListener('keydown', (e) => { if(e.key === 'Delete' || e.key === 'Backspace') { if(selectedId) deleteSelected(); } else if(e.key === 'v') setTool('select'); else if(e.key === 'r') setTool('rect'); else if(e.key === 'e') setTool('ellipse'); else if(e.key === 'l') setTool('line'); else if(e.key === 'd' && e.ctrlKey) { e.preventDefault(); duplicateSelected(); } }); // ==================== INIT ==================== window.addEventListener('load', () => { // Set canvas size const rect = svg.parentElement.getBoundingClientRect(); svg.setAttribute('viewBox', '0 0 ' + rect.width + ' ' + rect.height); });