viewer.html 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. <!DOCTYPE html>
  2. <!--
  3. THIS IS A TEMPLATE THAT SHOULD BE USED EVERY TIME AND MODIFIED.
  4. WHAT TO KEEP:
  5. ✓ Overall structure (header, sidebar, main content)
  6. ✓ Anthropic branding (colors, fonts, layout)
  7. ✓ Seed navigation section (always include this)
  8. ✓ Self-contained artifact (everything inline)
  9. WHAT TO CREATIVELY EDIT:
  10. ✗ The p5.js algorithm (implement YOUR vision)
  11. ✗ The parameters (define what YOUR art needs)
  12. ✗ The UI controls (match YOUR parameters)
  13. Let your philosophy guide the implementation.
  14. The world is your oyster - be creative!
  15. -->
  16. <html lang="en">
  17. <head>
  18. <meta charset="UTF-8">
  19. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  20. <title>Generative Art Viewer</title>
  21. <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
  22. <link rel="preconnect" href="https://fonts.googleapis.com">
  23. <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  24. <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
  25. <style>
  26. /* Anthropic Brand Colors */
  27. :root {
  28. --anthropic-dark: #141413;
  29. --anthropic-light: #faf9f5;
  30. --anthropic-mid-gray: #b0aea5;
  31. --anthropic-light-gray: #e8e6dc;
  32. --anthropic-orange: #d97757;
  33. --anthropic-blue: #6a9bcc;
  34. --anthropic-green: #788c5d;
  35. }
  36. * {
  37. margin: 0;
  38. padding: 0;
  39. box-sizing: border-box;
  40. }
  41. body {
  42. font-family: 'Poppins', sans-serif;
  43. background: linear-gradient(135deg, var(--anthropic-light) 0%, #f5f3ee 100%);
  44. min-height: 100vh;
  45. color: var(--anthropic-dark);
  46. }
  47. .container {
  48. display: flex;
  49. min-height: 100vh;
  50. padding: 20px;
  51. gap: 20px;
  52. }
  53. /* Sidebar */
  54. .sidebar {
  55. width: 320px;
  56. flex-shrink: 0;
  57. background: rgba(255, 255, 255, 0.95);
  58. backdrop-filter: blur(10px);
  59. padding: 24px;
  60. border-radius: 12px;
  61. box-shadow: 0 10px 30px rgba(20, 20, 19, 0.1);
  62. overflow-y: auto;
  63. overflow-x: hidden;
  64. }
  65. .sidebar h1 {
  66. font-family: 'Lora', serif;
  67. font-size: 24px;
  68. font-weight: 500;
  69. color: var(--anthropic-dark);
  70. margin-bottom: 8px;
  71. }
  72. .sidebar .subtitle {
  73. color: var(--anthropic-mid-gray);
  74. font-size: 14px;
  75. margin-bottom: 32px;
  76. line-height: 1.4;
  77. }
  78. /* Control Sections */
  79. .control-section {
  80. margin-bottom: 32px;
  81. }
  82. .control-section h3 {
  83. font-size: 16px;
  84. font-weight: 600;
  85. color: var(--anthropic-dark);
  86. margin-bottom: 16px;
  87. display: flex;
  88. align-items: center;
  89. gap: 8px;
  90. }
  91. .control-section h3::before {
  92. content: '•';
  93. color: var(--anthropic-orange);
  94. font-weight: bold;
  95. }
  96. /* Seed Controls */
  97. .seed-input {
  98. width: 100%;
  99. background: var(--anthropic-light);
  100. padding: 12px;
  101. border-radius: 8px;
  102. font-family: 'Courier New', monospace;
  103. font-size: 14px;
  104. margin-bottom: 12px;
  105. border: 1px solid var(--anthropic-light-gray);
  106. text-align: center;
  107. }
  108. .seed-input:focus {
  109. outline: none;
  110. border-color: var(--anthropic-orange);
  111. box-shadow: 0 0 0 2px rgba(217, 119, 87, 0.1);
  112. background: white;
  113. }
  114. .seed-controls {
  115. display: grid;
  116. grid-template-columns: 1fr 1fr;
  117. gap: 8px;
  118. margin-bottom: 8px;
  119. }
  120. .regen-button {
  121. margin-bottom: 0;
  122. }
  123. /* Parameter Controls */
  124. .control-group {
  125. margin-bottom: 20px;
  126. }
  127. .control-group label {
  128. display: block;
  129. font-size: 14px;
  130. font-weight: 500;
  131. color: var(--anthropic-dark);
  132. margin-bottom: 8px;
  133. }
  134. .slider-container {
  135. display: flex;
  136. align-items: center;
  137. gap: 12px;
  138. }
  139. .slider-container input[type="range"] {
  140. flex: 1;
  141. height: 4px;
  142. background: var(--anthropic-light-gray);
  143. border-radius: 2px;
  144. outline: none;
  145. -webkit-appearance: none;
  146. }
  147. .slider-container input[type="range"]::-webkit-slider-thumb {
  148. -webkit-appearance: none;
  149. width: 16px;
  150. height: 16px;
  151. background: var(--anthropic-orange);
  152. border-radius: 50%;
  153. cursor: pointer;
  154. transition: all 0.2s ease;
  155. }
  156. .slider-container input[type="range"]::-webkit-slider-thumb:hover {
  157. transform: scale(1.1);
  158. background: #c86641;
  159. }
  160. .slider-container input[type="range"]::-moz-range-thumb {
  161. width: 16px;
  162. height: 16px;
  163. background: var(--anthropic-orange);
  164. border-radius: 50%;
  165. border: none;
  166. cursor: pointer;
  167. transition: all 0.2s ease;
  168. }
  169. .value-display {
  170. font-family: 'Courier New', monospace;
  171. font-size: 12px;
  172. color: var(--anthropic-mid-gray);
  173. min-width: 60px;
  174. text-align: right;
  175. }
  176. /* Color Pickers */
  177. .color-group {
  178. margin-bottom: 16px;
  179. }
  180. .color-group label {
  181. display: block;
  182. font-size: 12px;
  183. color: var(--anthropic-mid-gray);
  184. margin-bottom: 4px;
  185. }
  186. .color-picker-container {
  187. display: flex;
  188. align-items: center;
  189. gap: 8px;
  190. }
  191. .color-picker-container input[type="color"] {
  192. width: 32px;
  193. height: 32px;
  194. border: none;
  195. border-radius: 6px;
  196. cursor: pointer;
  197. background: none;
  198. padding: 0;
  199. }
  200. .color-value {
  201. font-family: 'Courier New', monospace;
  202. font-size: 12px;
  203. color: var(--anthropic-mid-gray);
  204. }
  205. /* Buttons */
  206. .button {
  207. background: var(--anthropic-orange);
  208. color: white;
  209. border: none;
  210. padding: 10px 16px;
  211. border-radius: 6px;
  212. font-size: 14px;
  213. font-weight: 500;
  214. cursor: pointer;
  215. transition: all 0.2s ease;
  216. width: 100%;
  217. }
  218. .button:hover {
  219. background: #c86641;
  220. transform: translateY(-1px);
  221. }
  222. .button:active {
  223. transform: translateY(0);
  224. }
  225. .button.secondary {
  226. background: var(--anthropic-blue);
  227. }
  228. .button.secondary:hover {
  229. background: #5a8bb8;
  230. }
  231. .button.tertiary {
  232. background: var(--anthropic-green);
  233. }
  234. .button.tertiary:hover {
  235. background: #6b7b52;
  236. }
  237. .button-row {
  238. display: flex;
  239. gap: 8px;
  240. }
  241. .button-row .button {
  242. flex: 1;
  243. }
  244. /* Canvas Area */
  245. .canvas-area {
  246. flex: 1;
  247. display: flex;
  248. align-items: center;
  249. justify-content: center;
  250. min-width: 0;
  251. }
  252. #canvas-container {
  253. width: 100%;
  254. max-width: 1000px;
  255. border-radius: 12px;
  256. overflow: hidden;
  257. box-shadow: 0 20px 40px rgba(20, 20, 19, 0.1);
  258. background: white;
  259. }
  260. #canvas-container canvas {
  261. display: block;
  262. width: 100% !important;
  263. height: auto !important;
  264. }
  265. /* Loading State */
  266. .loading {
  267. display: flex;
  268. align-items: center;
  269. justify-content: center;
  270. font-size: 18px;
  271. color: var(--anthropic-mid-gray);
  272. }
  273. /* Responsive - Stack on mobile */
  274. @media (max-width: 600px) {
  275. .container {
  276. flex-direction: column;
  277. }
  278. .sidebar {
  279. width: 100%;
  280. }
  281. .canvas-area {
  282. padding: 20px;
  283. }
  284. }
  285. </style>
  286. </head>
  287. <body>
  288. <div class="container">
  289. <!-- Control Sidebar -->
  290. <div class="sidebar">
  291. <!-- Headers (CUSTOMIZE THIS FOR YOUR ART) -->
  292. <h1>TITLE - EDIT</h1>
  293. <div class="subtitle">SUBHEADER - EDIT</div>
  294. <!-- Seed Section (ALWAYS KEEP THIS) -->
  295. <div class="control-section">
  296. <h3>Seed</h3>
  297. <input type="number" id="seed-input" class="seed-input" value="12345" onchange="updateSeed()">
  298. <div class="seed-controls">
  299. <button class="button secondary" onclick="previousSeed()">← Prev</button>
  300. <button class="button secondary" onclick="nextSeed()">Next →</button>
  301. </div>
  302. <button class="button tertiary regen-button" onclick="randomSeedAndUpdate()">↻ Random</button>
  303. </div>
  304. <!-- Parameters Section (CUSTOMIZE THIS FOR YOUR ART) -->
  305. <div class="control-section">
  306. <h3>Parameters</h3>
  307. <!-- Particle Count -->
  308. <div class="control-group">
  309. <label>Particle Count</label>
  310. <div class="slider-container">
  311. <input type="range" id="particleCount" min="1000" max="10000" step="500" value="5000" oninput="updateParam('particleCount', this.value)">
  312. <span class="value-display" id="particleCount-value">5000</span>
  313. </div>
  314. </div>
  315. <!-- Flow Speed -->
  316. <div class="control-group">
  317. <label>Flow Speed</label>
  318. <div class="slider-container">
  319. <input type="range" id="flowSpeed" min="0.1" max="2.0" step="0.1" value="0.5" oninput="updateParam('flowSpeed', this.value)">
  320. <span class="value-display" id="flowSpeed-value">0.5</span>
  321. </div>
  322. </div>
  323. <!-- Noise Scale -->
  324. <div class="control-group">
  325. <label>Noise Scale</label>
  326. <div class="slider-container">
  327. <input type="range" id="noiseScale" min="0.001" max="0.02" step="0.001" value="0.005" oninput="updateParam('noiseScale', this.value)">
  328. <span class="value-display" id="noiseScale-value">0.005</span>
  329. </div>
  330. </div>
  331. <!-- Trail Length -->
  332. <div class="control-group">
  333. <label>Trail Length</label>
  334. <div class="slider-container">
  335. <input type="range" id="trailLength" min="2" max="20" step="1" value="8" oninput="updateParam('trailLength', this.value)">
  336. <span class="value-display" id="trailLength-value">8</span>
  337. </div>
  338. </div>
  339. </div>
  340. <!-- Colors Section (OPTIONAL - CUSTOMIZE OR REMOVE) -->
  341. <div class="control-section">
  342. <h3>Colors</h3>
  343. <!-- Color 1 -->
  344. <div class="color-group">
  345. <label>Primary Color</label>
  346. <div class="color-picker-container">
  347. <input type="color" id="color1" value="#d97757" onchange="updateColor('color1', this.value)">
  348. <span class="color-value" id="color1-value">#d97757</span>
  349. </div>
  350. </div>
  351. <!-- Color 2 -->
  352. <div class="color-group">
  353. <label>Secondary Color</label>
  354. <div class="color-picker-container">
  355. <input type="color" id="color2" value="#6a9bcc" onchange="updateColor('color2', this.value)">
  356. <span class="color-value" id="color2-value">#6a9bcc</span>
  357. </div>
  358. </div>
  359. <!-- Color 3 -->
  360. <div class="color-group">
  361. <label>Accent Color</label>
  362. <div class="color-picker-container">
  363. <input type="color" id="color3" value="#788c5d" onchange="updateColor('color3', this.value)">
  364. <span class="color-value" id="color3-value">#788c5d</span>
  365. </div>
  366. </div>
  367. </div>
  368. <!-- Actions Section (ALWAYS KEEP THIS) -->
  369. <div class="control-section">
  370. <h3>Actions</h3>
  371. <div class="button-row">
  372. <button class="button" onclick="resetParameters()">Reset</button>
  373. </div>
  374. </div>
  375. </div>
  376. <!-- Main Canvas Area -->
  377. <div class="canvas-area">
  378. <div id="canvas-container">
  379. <div class="loading">Initializing generative art...</div>
  380. </div>
  381. </div>
  382. </div>
  383. <script>
  384. // ═══════════════════════════════════════════════════════════════════════
  385. // GENERATIVE ART PARAMETERS - CUSTOMIZE FOR YOUR ALGORITHM
  386. // ═══════════════════════════════════════════════════════════════════════
  387. let params = {
  388. seed: 12345,
  389. particleCount: 5000,
  390. flowSpeed: 0.5,
  391. noiseScale: 0.005,
  392. trailLength: 8,
  393. colorPalette: ['#d97757', '#6a9bcc', '#788c5d']
  394. };
  395. let defaultParams = {...params}; // Store defaults for reset
  396. // ═══════════════════════════════════════════════════════════════════════
  397. // P5.JS GENERATIVE ART ALGORITHM - REPLACE WITH YOUR VISION
  398. // ═══════════════════════════════════════════════════════════════════════
  399. let particles = [];
  400. let flowField = [];
  401. let cols, rows;
  402. let scl = 10; // Flow field resolution
  403. function setup() {
  404. let canvas = createCanvas(1200, 1200);
  405. canvas.parent('canvas-container');
  406. initializeSystem();
  407. // Remove loading message
  408. document.querySelector('.loading').style.display = 'none';
  409. }
  410. function initializeSystem() {
  411. // Seed the randomness for reproducibility
  412. randomSeed(params.seed);
  413. noiseSeed(params.seed);
  414. // Clear particles and recreate
  415. particles = [];
  416. // Initialize particles
  417. for (let i = 0; i < params.particleCount; i++) {
  418. particles.push(new Particle());
  419. }
  420. // Calculate flow field dimensions
  421. cols = floor(width / scl);
  422. rows = floor(height / scl);
  423. // Generate flow field
  424. generateFlowField();
  425. // Clear background
  426. background(250, 249, 245); // Anthropic light background
  427. }
  428. function generateFlowField() {
  429. // fill this in
  430. }
  431. function draw() {
  432. // fill this in
  433. }
  434. // ═══════════════════════════════════════════════════════════════════════
  435. // PARTICLE SYSTEM - CUSTOMIZE FOR YOUR ALGORITHM
  436. // ═══════════════════════════════════════════════════════════════════════
  437. class Particle {
  438. constructor() {
  439. // fill this in
  440. }
  441. // fill this in
  442. }
  443. // ═══════════════════════════════════════════════════════════════════════
  444. // UI CONTROL HANDLERS - CUSTOMIZE FOR YOUR PARAMETERS
  445. // ═══════════════════════════════════════════════════════════════════════
  446. function updateParam(paramName, value) {
  447. // fill this in
  448. }
  449. function updateColor(colorId, value) {
  450. // fill this in
  451. }
  452. // ═══════════════════════════════════════════════════════════════════════
  453. // SEED CONTROL FUNCTIONS - ALWAYS KEEP THESE
  454. // ═══════════════════════════════════════════════════════════════════════
  455. function updateSeedDisplay() {
  456. document.getElementById('seed-input').value = params.seed;
  457. }
  458. function updateSeed() {
  459. let input = document.getElementById('seed-input');
  460. let newSeed = parseInt(input.value);
  461. if (newSeed && newSeed > 0) {
  462. params.seed = newSeed;
  463. initializeSystem();
  464. } else {
  465. // Reset to current seed if invalid
  466. updateSeedDisplay();
  467. }
  468. }
  469. function previousSeed() {
  470. params.seed = Math.max(1, params.seed - 1);
  471. updateSeedDisplay();
  472. initializeSystem();
  473. }
  474. function nextSeed() {
  475. params.seed = params.seed + 1;
  476. updateSeedDisplay();
  477. initializeSystem();
  478. }
  479. function randomSeedAndUpdate() {
  480. params.seed = Math.floor(Math.random() * 999999) + 1;
  481. updateSeedDisplay();
  482. initializeSystem();
  483. }
  484. function resetParameters() {
  485. params = {...defaultParams};
  486. // Update UI elements
  487. document.getElementById('particleCount').value = params.particleCount;
  488. document.getElementById('particleCount-value').textContent = params.particleCount;
  489. document.getElementById('flowSpeed').value = params.flowSpeed;
  490. document.getElementById('flowSpeed-value').textContent = params.flowSpeed;
  491. document.getElementById('noiseScale').value = params.noiseScale;
  492. document.getElementById('noiseScale-value').textContent = params.noiseScale;
  493. document.getElementById('trailLength').value = params.trailLength;
  494. document.getElementById('trailLength-value').textContent = params.trailLength;
  495. // Reset colors
  496. document.getElementById('color1').value = params.colorPalette[0];
  497. document.getElementById('color1-value').textContent = params.colorPalette[0];
  498. document.getElementById('color2').value = params.colorPalette[1];
  499. document.getElementById('color2-value').textContent = params.colorPalette[1];
  500. document.getElementById('color3').value = params.colorPalette[2];
  501. document.getElementById('color3-value').textContent = params.colorPalette[2];
  502. updateSeedDisplay();
  503. initializeSystem();
  504. }
  505. // Initialize UI on load
  506. window.addEventListener('load', function() {
  507. updateSeedDisplay();
  508. });
  509. </script>
  510. </body>
  511. </html>