]> git.cworth.org Git - zombocom-ai/blob - index.html
Reword Coda's final message
[zombocom-ai] / index.html
1 <!DOCTYPE html>
2 <html>
3
4 <head>
5   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6   <title>ZOMBO</title>
7   <link href="/zombo.css" rel="stylesheet" type="text/css">
8   <meta name="viewport" content="width=device-width,initial-scale=1">
9   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
10   <meta name="HandheldFriendly" content="true">
11 </head>
12
13 <body>
14   <div id="content">
15
16     <div id="header" align="center">
17       <p>
18         <br>
19       </p>
20
21       <p>
22         <img src="/zombocom.png" alt="Zombocom" longdesc="http://zombo.com" width="1199" height="217">
23       </p>
24     </div>
25
26     <p>
27       Welcome to Zombocom. You can do anything at Zombocom, anything at
28       all. The only limit is yourself!
29     </p>
30
31     <dialog id="name-dialog">
32       <form method="dialog">
33         <p>
34           <label>Your name:
35             <input id="name" type="text" autocomplete="off">
36           </label>
37         </p>
38         <div>
39           <button value="default">OK</button>
40         </div>
41       </form>
42     </dialog>
43
44     <p>
45       <div id="spinner" align="center">
46         <div class="animate-flicker">
47           <p>
48             <img src="/pngwheel.png" class="rotate thefade">
49           </p>
50         </div>
51       </div>
52
53       <form action="" id="zombo-form">
54         <div class="form-row large">
55           <label for="prompt">
56             What can you imagine?
57           </label>
58           <button id="safety" type="button">Safety prompt</button>
59           <textarea id="prompt" rows="4" width="100%" autocomplete="off" required></textarea>
60         </div>
61
62         <div class="form-row small left">
63           <div class="labeled-row">
64             <label for="code">
65               Numeric code:
66             </label>
67             <input id="code" type="number" min="0" max="4294967295" autocomplete="off" placeholder="(Leave blank for random)" />
68           </div>
69         </div>
70
71         <div class="form-row large">
72           <button id="generate" type="submit">Make the infinite possible</button>
73         </div>
74       </form>
75     </p>
76
77     <div id="images">
78     </div>
79
80     <audio loop="" src="/zombo_words.mp3" type="audio/mpeg"></audio>
81     <button id="mute" class="menu-button fade volume">
82       <div>🔊</div>
83       <script>
84 const mute = document.querySelector("#mute");
85 const icon = document.querySelector("#mute > div");
86 const audio = document.querySelector("audio");
87
88 mute.addEventListener("click", () => {
89   if (audio.paused) {
90       audio.volume = 0.2;
91       audio.play();
92       icon.innerHTML = "🔈";
93   } else {
94       audio.pause();
95       icon.innerHTML = "🔊";
96   }
97   mute.classList.add("fade");
98 });
99       </script>
100
101     </button>
102
103     <button id="profile" class="menu-button">
104       <div>👤</div>
105     </button>
106
107   </div>
108
109   <script src="/socket.io/socket.io.js"></script>
110   <script>
111     var socket = io();
112
113     const name_input = document.querySelector("#name");
114     const name_dialog = document.querySelector("#name-dialog");
115     var informed_name;
116     const images = document.querySelector("#images");
117     const zombo_form = document.querySelector("#zombo-form");
118     const prompt = document.querySelector("#prompt");
119     const code = document.querySelector("#code");
120     const safety= document.querySelector("#safety");
121     const spinner = document.querySelector("#spinner");
122     const profile = document.querySelector("#profile");
123     var spinner_timeout;
124
125     profile.addEventListener('click', () => {
126         name_dialog.showModal();
127     });
128
129     name_dialog.addEventListener('close', () => {
130         socket.emit('set-name', name_input.value);
131     });
132
133     function add_comment(comments, comment) {
134         const dt = document.createElement('dt');
135         const dd = document.createElement('dd');
136         dt.textContent = comment.name + ':';
137         dd.textContent = comment.text;
138         comments.appendChild(dt);
139         comments.appendChild(dd);
140     }
141
142     // When we enter the endgame, we disable the art-generation form,
143     // (just hide it)
144     socket.on('endgame', () => {
145         zombo_form.style.display = "none";
146     });
147
148     socket.on('comment', function(comment) {
149         const comments = document.querySelector("#comments_" + comment.image_id);
150         add_comment(comments, comment);
151     });
152
153     socket.on('inform-name', (name) => {
154         console.log("Received inform-name event: " + name);
155
156         /* When we receive a name we store it in 3 places:
157          *
158          *   * The informed_name variable (confirming what the server knows)
159          *   * The name_input field (so the user will see that in their profile)
160          *   * In localStorage (for use in a future session)
161          */
162         informed_name = name;
163         name_input.value = name;
164         localStorage.setItem('name', name);
165     });
166
167     socket.on('reset', () => {
168         images.replaceChildren();
169
170         /* Since the server is seeing us for the first time, let it
171            know our name (if we have one). */
172         const name = localStorage.getItem('name');
173         if (name) {
174             socket.emit('set-name', name);
175             return;
176         }
177     });
178
179     socket.on('image', (image) => {
180         const figure = document.createElement('figure');
181         figure.id = "image_" + image.id;
182
183         const img = document.createElement('img');
184         img.src = image.filename;
185
186         if (image.link) {
187             const a = document.createElement('a');
188             a.href = image.link;
189             a.appendChild(img);
190             figure.appendChild(a);
191         } else {
192             figure.appendChild(img);
193         }
194
195         if (image.censored) {
196             img.style.display = "none";
197         }
198
199         if (image.prompt && image.code) {
200             const figcaption = document.createElement('figcaption');
201             const caption_text = document.createTextNode(`${image.prompt} (${image.code}) `);
202             figcaption.appendChild(caption_text);
203
204             const reuse_button = document.createElement('button');
205             reuse_button.appendChild(document.createTextNode("Reuse"));
206             figcaption.appendChild(reuse_button);
207
208             reuse_button.addEventListener('click', () => {
209                 prompt.value = image.prompt;
210                 window.scrollTo(0,0);
211             });
212
213             if (image.censored) {
214                 figcaption.style.display = "none";
215             }
216
217             figure.appendChild(figcaption);
218         }
219
220         const dl_comments = document.createElement('dl');
221         dl_comments.className = "comments";
222         dl_comments.id = "comments_" + image.id;
223
224         image.comments.forEach((comment) => {
225             add_comment(dl_comments, comment);
226         });
227
228         figure.appendChild(dl_comments);
229
230         const comment_form = document.createElement('form');
231         const comment_input = document.createElement('input')
232         comment_input.type = "text";
233         comment_input.className = "comment";
234         comment_input.placeholder = "Add a comment";
235         comment_form.appendChild(comment_input);
236         figure.appendChild(comment_form);
237
238         comment_input.addEventListener('focus', () => {
239             /* If the server has informed us it has our name, we are done. */
240             if (informed_name)
241                 return;
242
243             /* If server has no name, check local storage. */
244             const name = localStorage.getItem('name');
245             if (name) {
246                 socket.emit('set-name', name);
247                 return;
248             }
249
250             /* Failing both, bring up the modal dialog to set the name. */
251             name_dialog.showModal();
252         });
253
254         comment_form.addEventListener('submit', function(e) {
255             e.preventDefault();
256             if (comment_input.value) {
257                 socket.emit('comment', {image_id: image.id,
258                                         text: comment_input.value});
259                 comment_input.value = '';
260             }
261         });
262
263         images.prepend(figure);
264     });
265
266     function hide_spinner() {
267         zombo_form.style.display = "grid";
268         spinner.style.display = "none";
269     }
270
271     zombo_form.addEventListener('submit', function(e) {
272         e.preventDefault();
273         /* Hide the form and show spinner while generation is happening. */
274         zombo_form.style.display = "none";
275         spinner.style.display = "block";
276         spinner_timeout = setTimeout(hide_spinner, 60000);
277         socket.emit('generate', {"prompt": prompt.value, "code": code.value});
278         prompt.value = '';
279     });
280
281     socket.on('generation-done', () => {
282         /* Re-display the form and hide spinner now that generation is over. */
283         clearTimeout(spinner_timeout);
284         hide_spinner();
285     });
286
287     function random_from(items) {
288         return items[Math.floor(Math.random()*items.length)];
289     }
290
291     safety.addEventListener("click", () => {
292         const art_type = [
293             "A portrait of",
294             "A hyperrealistic photograph of",
295             "A matte painting of",
296             "Epic fantasy card art of",
297             "Professional oil painting of",
298             "An image of",
299             "A pencil sketch of",
300             "A watercolor of",
301             "A 3D rendering of",
302             "A marble statue of",
303         ];
304         const subject = [
305             " a modern home",
306             " crashing waves",
307             " a Porsche 911 Carrera",
308             " a Halo spartan",
309             " a cute cat",
310             " a mad scientist",
311             " an elfin princess",
312             " sci-fi buildings",
313             " pastel purple clouds",
314             " a Teenage Mutant Ninja Turtle",
315             " Pikachu",
316         ];
317         const scenery = [
318             " in tropical surroundings",
319             " in space",
320             " in an ocean storm",
321             " in a snowy forest",
322             " in a cardboard box",
323             " holding an axe",
324             " in an epic landscape",
325             " in a haunted forest",
326             " riding a motorbike",
327             " in a futuristic city at night",
328             " in a dystopian landscape, foggy",
329             " in the land of snow",
330             " that looks like a watermelon",
331             " on the streets of London at night with neon lights flashing",
332             " that is part octopus",
333             " melting into a puddle",
334             " on a birthday cake",
335         ];
336         const artist = [
337             ", unreal engine 5",
338             ", concept art",
339             ", graphic novel",
340             " by Vincent Van Gogh",
341             " by Claude Monet",
342             " by Johannes Vermeer",
343             ", fantasy art, neon fog",
344             ", epic lighting from above",
345             ", trending on artstation",
346             " by Gustav Klimt",
347             " by Leonardo da Vinci",
348         ];
349
350         prompt.value = random_from(art_type) + random_from(subject) +
351             random_from(scenery) + random_from(artist);
352         return false;
353     });
354
355   </script>
356 </body>
357 </html>