跳到主要内容

可调参数的生成艺术

收藏0

用 p5.js 创建粒子、流场与几何生成艺术,输出可调参数、切换随机种子并导出 PNG 的网页作品。

让 AI 安装

github最新版本 0.1.1更新于 2026-10-21下载 3 次2 个版本

templates/viewer.html

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

文件内容是平台检查时读出的文本,只供查看。平台不会运行包里的任何脚本或程序。