ZoroaStrella commited on
Commit
c69d8f2
·
verified ·
1 Parent(s): 54eca5f

Create a mobile-ready, responsive app designed primarily for tablets and phones, with a UI to help dungeon masters select and play sounds together to create immersive atmospheres during gameplay. Use local forage, consider that we will run this in webview or progressive webapp, consider options for those persistence Users can: - Create projects and playlists, pads - a project may have many playlist, a pad is a collection of songs and sounds, noises that can play together, simultaneously or played as clicked with a UI - Search for projects, songs, videos, sounds, or playlists. - Add, delete, and organize sounds, videos, or songs in projects and playlists. - Handle YouTube, SoundCloud, Vimeo, and Dailymotion URLs for external audio and video sources. - Support multiple sound formats like m4a, wav, mp3, mp4, and playlists. - Arrange sounds and scroll through project or playlist items. - Play multiple sounds, songs, or videos simultaneously. - Use a pad-like interface to load sounds, videos, or songs and edit settings, such as selecting sound segments like a live sampler like a daw workstation such as ableton wit hminimalism and ux in mind. - Create sections within playlists for easy navigation. - Include basic atmospheric sounds and noise generators. - Automatically or manually tag and categorize sounds, videos, and songs for easy searching. - Handle transition properties in advance or live mode. This app enables dungeon masters to craft sonic soundscapes, access fast sounds, or use pre-prepared playlists while optimizing background processes for smooth performance on phones. It allows simultaneous playback and searching of playlists and sounds without restrictions. Optimize data loading and buffering, and provide basic sound editing with signal processing features. Use the best available TypeScript audio library and React for development. The design should feature gold and purple colors with medieval fonts. Include specific tags for various game sounds and background music, such as OSTs and original soundtracks for films and games. Add pre-built TTRPG, D&D playlists, draw inspiration from existing products with similar features, and prioritize user experience with bottom navigation and action buttons. - Initial Deployment

Browse files
Files changed (3) hide show
  1. README.md +6 -4
  2. index.html +731 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Rawdio
3
- emoji: 🏆
4
  colorFrom: pink
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: rawdio
3
+ emoji: 🐳
4
  colorFrom: pink
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,731 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Dungeon Master Soundscapes</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/howler.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <link rel="preconnect" href="https://fonts.googleapis.com">
12
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
+ <link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=MedievalSharp&display=swap" rel="stylesheet">
14
+
15
+ <script>
16
+ tailwind.config = {
17
+ theme: {
18
+ extend: {
19
+ colors: {
20
+ primary: '#D4AF37', // Gold
21
+ secondary: '#5C2D91', // Purple
22
+ dark: '#1A0B23',
23
+ light: '#F2EBD3'
24
+ },
25
+ fontFamily: {
26
+ medieval: ['Cinzel', 'MedievalSharp', 'serif'],
27
+ body: ['Roboto', 'sans-serif']
28
+ },
29
+ backgroundImage: {
30
+ 'paper-texture': "url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MDAiIGhlaWdodD0iNTAwIj4KICA8cmVjdCB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgZmlsbD0iI0YyRUJEMyIvPgogIDxwYXRoIGQ9Ik0gMCwwIEwgMCw1MDAgTCA1MDAsNTAwIEwgNTAwLDAgeiIgc3Ryb2tlPSIjRkZGIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiLz4KICA8cGF0aCBkPSJNIDI1LDI1IGMgMTAsMTAgMTUsMjAgMTUsMzAgIiBzdHJva2U9IiNGOEYyREIiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgb3BhY2l0eT0iMC4yIi8+Cjwvc3ZnPg==')"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ </script>
36
+
37
+ <style type="text/css">
38
+ body {
39
+ font-family: 'Roboto', sans-serif;
40
+ background: #1A0B23;
41
+ color: #F2EBD3;
42
+ overflow-x: hidden;
43
+ touch-action: manipulation;
44
+ }
45
+
46
+ .medieval-font {
47
+ font-family: 'Cinzel', 'MedievalSharp', serif;
48
+ letter-spacing: 0.5px;
49
+ }
50
+
51
+ .scrollbar-hide::-webkit-scrollbar {
52
+ display: none;
53
+ }
54
+
55
+ .scrollbar-hide {
56
+ -ms-overflow-style: none;
57
+ scrollbar-width: none;
58
+ }
59
+
60
+ .gradient-border {
61
+ border: 2px solid transparent;
62
+ background-clip: padding-box;
63
+ position: relative;
64
+ background: #1A0B23;
65
+ }
66
+
67
+ .gradient-border::before {
68
+ content: '';
69
+ position: absolute;
70
+ top: -2px;
71
+ left: -2px;
72
+ right: -2px;
73
+ bottom: -2px;
74
+ background: linear-gradient(135deg, #D4AF37, #5C2D91);
75
+ z-index: -1;
76
+ border-radius: inherit;
77
+ }
78
+
79
+ .sound-item:hover, .pad:hover {
80
+ transform: scale(1.02);
81
+ box-shadow: 0 4px 15px rgba(212, 175, 55, 0.3);
82
+ transition: all 0.2s ease;
83
+ }
84
+
85
+ .playing {
86
+ box-shadow: 0 0 0 3px rgba(212, 175, 55, 0.6);
87
+ animation: pulse 1.5s infinite;
88
+ }
89
+
90
+ @keyframes pulse {
91
+ 0% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0.6); }
92
+ 70% { box-shadow: 0 0 0 10px rgba(212, 175, 55, 0); }
93
+ 100% { box-shadow: 0 0 0 0 rgba(212, 175, 55, 0); }
94
+ }
95
+
96
+ .volume-slider {
97
+ -webkit-appearance: none;
98
+ appearance: none;
99
+ height: 6px;
100
+ border-radius: 3px;
101
+ background: #5C2D91;
102
+ outline: none;
103
+ }
104
+
105
+ .volume-slider::-webkit-slider-thumb {
106
+ -webkit-appearance: none;
107
+ appearance: none;
108
+ width: 18px;
109
+ height: 18px;
110
+ border-radius: 50%;
111
+ background: #D4AF37;
112
+ cursor: pointer;
113
+ }
114
+
115
+ .sound-wave {
116
+ display: flex;
117
+ align-items: flex-end;
118
+ justify-content: space-between;
119
+ height: 40px;
120
+ width: 100%;
121
+ }
122
+
123
+ .sound-bar {
124
+ width: 3px;
125
+ background-color: #D4AF37;
126
+ border-radius: 2px;
127
+ margin: 0 1px;
128
+ }
129
+
130
+ .page-enter {
131
+ animation: fadeIn 0.3s forwards;
132
+ }
133
+
134
+ @keyframes fadeIn {
135
+ from { opacity: 0; transform: translateY(10px); }
136
+ to { opacity: 1; transform: translateY(0); }
137
+ }
138
+
139
+ @media (max-width: 640px) {
140
+ .bottom-nav {
141
+ box-shadow: 0 -3px 10px rgba(0,0,0,0.2);
142
+ }
143
+ }
144
+ </style>
145
+ </head>
146
+ <body class="min-h-screen bg-gradient-to-b from-dark to-secondary">
147
+ <div id="app" class="flex flex-col min-h-screen max-w-full overflow-hidden">
148
+ <!-- Top Navigation Bar -->
149
+ <header class="gradient-border px-4 py-3 flex items-center justify-between">
150
+ <button id="menuBtn" class="text-primary text-2xl">
151
+ <i class="fas fa-bars"></i>
152
+ </button>
153
+ <h1 class="medieval-font text-2xl text-primary text-center font-bold">
154
+ Dungeon Master Soundscapes
155
+ </h1>
156
+ <button class="text-primary text-2xl">
157
+ <i class="fas fa-user"></i>
158
+ </button>
159
+ </header>
160
+
161
+ <!-- Main Content Area -->
162
+ <main class="flex-grow overflow-hidden relative" style="background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cGF0aCBkPSJNMCAwTDEwMCAwTDEwMCAxMDBMMCAxMDBaTTAgMEw1MCA1MFo='); background-size: 20px 20px; background-color: #1A0B23; background-blend-mode: overlay; opacity: 0.15;">
163
+ <div id="page-content" class="absolute inset-0 overflow-auto scrollbar-hide p-4">
164
+ <!-- Dashboard Page will be inserted here -->
165
+ </div>
166
+ </main>
167
+
168
+ <!-- Bottom Navigation -->
169
+ <nav id="bottomNav" class="bottom-nav bg-secondary flex justify-around py-2 border-t border-primary">
170
+ <button data-page="dashboard" class="nav-item flex flex-col items-center text-primary hover:text-white">
171
+ <i class="fas fa-home text-xl"></i>
172
+ <span class="text-xs mt-1">Dashboard</span>
173
+ </button>
174
+ <button data-page="sounds" class="nav-item flex flex-col items-center text-primary hover:text-white">
175
+ <i class="fas fa-music text-xl"></i>
176
+ <span class="text-xs mt-1">Sounds</span>
177
+ </button>
178
+ <button data-page="pad" class="nav-item flex flex-col items-center text-primary hover:text-white">
179
+ <i class="fas fa-th-large text-xl"></i>
180
+ <span class="text-xs mt-1">Sound Pad</span>
181
+ </button>
182
+ <button data-page="playlists" class="nav-item flex flex-col items-center text-primary hover:text-white">
183
+ <i class="fas fa-list text-xl"></i>
184
+ <span class="text-xs mt-1">Playlists</span>
185
+ </button>
186
+ <button data-page="projects" class="nav-item flex flex-col items-center text-primary hover:text-white">
187
+ <i class="fas fa-folder text-xl"></i>
188
+ <span class="text-xs mt-1">Projects</span>
189
+ </button>
190
+ </nav>
191
+ </div>
192
+
193
+ <script>
194
+ // Main app state
195
+ const state = {
196
+ currentPage: 'dashboard',
197
+ sounds: [],
198
+ playlists: [],
199
+ projects: [],
200
+ currentProject: null,
201
+ playingSounds: {},
202
+ volume: 0.7
203
+ };
204
+
205
+ // DOM Elements
206
+ const elements = {
207
+ pageContent: document.getElementById('page-content'),
208
+ bottomNav: document.getElementById('bottomNav'),
209
+ menuBtn: document.getElementById('menuBtn')
210
+ };
211
+
212
+ // Initialize the app
213
+ function initApp() {
214
+ // Load state from localStorage
215
+ loadState();
216
+
217
+ // Setup navigation
218
+ setupNavigation();
219
+
220
+ // Render initial page
221
+ renderPage(state.currentPage);
222
+
223
+ // Load sample data if empty
224
+ setupSampleData();
225
+ }
226
+
227
+ // Setup bottom navigation
228
+ function setupNavigation() {
229
+ // Handle bottom nav clicks
230
+ elements.bottomNav.querySelectorAll('.nav-item').forEach(item => {
231
+ item.addEventListener('click', () => {
232
+ const page = item.dataset.page;
233
+ navigateTo(page);
234
+ });
235
+ });
236
+
237
+ // Handle menu button
238
+ elements.menuBtn.addEventListener('click', showMainMenu);
239
+ }
240
+
241
+ // Navigate to a page
242
+ function navigateTo(pageName) {
243
+ state.currentPage = pageName;
244
+ renderPage(pageName);
245
+
246
+ // Update active nav item
247
+ elements.bottomNav.querySelectorAll('.nav-item').forEach(item => {
248
+ const isActive = item.dataset.page === pageName;
249
+ item.classList.toggle('text-white', isActive);
250
+ item.classList.toggle('text-primary', !isActive);
251
+ });
252
+ }
253
+
254
+ // Render the current page
255
+ function renderPage(pageName) {
256
+ elements.pageContent.innerHTML = '';
257
+ elements.pageContent.classList.add('page-enter');
258
+
259
+ switch(pageName) {
260
+ case 'dashboard':
261
+ renderDashboard();
262
+ break;
263
+ case 'sounds':
264
+ renderSoundLibrary();
265
+ break;
266
+ case 'pad':
267
+ renderSoundPad();
268
+ break;
269
+ case 'playlists':
270
+ renderPlaylists();
271
+ break;
272
+ case 'projects':
273
+ renderProjects();
274
+ break;
275
+ default:
276
+ renderDashboard();
277
+ }
278
+ }
279
+
280
+ // Sample Pages Rendering Functions
281
+
282
+ function renderDashboard() {
283
+ const content = `
284
+ <div class="p-4">
285
+ <h2 class="medieval-font text-xl text-primary mb-4 font-bold">Current Project</h2>
286
+ ${renderProjectCard()}
287
+
288
+ <div class="mt-8">
289
+ <h2 class="medieval-font text-xl text-primary mb-4 font-bold">Quick Actions</h2>
290
+ <div class="grid grid-cols-2 gap-4">
291
+ <button class="bg-secondary py-4 rounded-lg flex flex-col items-center justify-center gradient-border">
292
+ <i class="fas fa-plus-circle text-primary text-3xl mb-2"></i>
293
+ <span class="text-primary">New Sound</span>
294
+ </button>
295
+ <button class="bg-secondary py-4 rounded-lg flex flex-col items-center justify-center gradient-border">
296
+ <i class="fas fa-headphones text-primary text-3xl mb-2"></i>
297
+ <span class="text-primary">Ambience</span>
298
+ </button>
299
+ <button class="bg-secondary py-4 rounded-lg flex flex-col items-center justify-center gradient-border">
300
+ <i class="fas fa-cloud-download-alt text-primary text-3xl mb-2"></i>
301
+ <span class="text-primary">Import</span>
302
+ </button>
303
+ <button class="bg-secondary py-4 rounded-lg flex flex-col items-center justify-center gradient-border">
304
+ <i class="fas fa-dragon text-primary text-3xl mb-2"></i>
305
+ <span class="text-primary">Monsters</span>
306
+ </button>
307
+ </div>
308
+ </div>
309
+
310
+ <div class="mt-8">
311
+ <h2 class="medieval-font text-xl text-primary mb-4 font-bold">Recent Sounds</h2>
312
+ <div class="grid grid-cols-3 gap-3">
313
+ ${state.sounds.slice(0, 6).map(sound => `
314
+ <div class="sound-item bg-secondary rounded-lg p-3 cursor-pointer gradient-border">
315
+ <div class="text-primary text-lg mb-2"><i class="fas ${sound.icon}"></i></div>
316
+ <p class="text-white truncate text-xs">${sound.name}</p>
317
+ </div>
318
+ `).join('')}
319
+ </div>
320
+ </div>
321
+ </div>
322
+ `;
323
+
324
+ elements.pageContent.innerHTML = content;
325
+ }
326
+
327
+ function renderProjectCard() {
328
+ if (!state.currentProject) {
329
+ return `
330
+ <div class="bg-secondary rounded-lg p-4 mb-4 gradient-border">
331
+ <p class="text-light mb-4">No project currently selected</p>
332
+ <button class="bg-primary hover:bg-opacity-90 text-dark py-2 px-4 rounded-md w-full medieval-font">
333
+ Create New Project
334
+ </button>
335
+ </div>
336
+ `;
337
+ }
338
+
339
+ const project = state.currentProject;
340
+ return `
341
+ <div class="bg-secondary rounded-lg p-4 mb-4 gradient-border">
342
+ <div class="flex justify-between items-center mb-3">
343
+ <h3 class="text-lg text-primary medieval-font">${project.name}</h3>
344
+ <span class="text-xs text-light">${project.lastEdited}</span>
345
+ </div>
346
+ <p class="text-light text-sm mb-4">${project.description}</p>
347
+ <div class="grid grid-cols-3 gap-2 text-center text-xs text-light">
348
+ <div class="bg-dark py-1 rounded">
349
+ <div class="text-primary font-bold">${project.playlists.length}</div>
350
+ <div>Playlists</div>
351
+ </div>
352
+ <div class="bg-dark py-1 rounded">
353
+ <div class="text-primary font-bold">${countProjectSounds(project)}</div>
354
+ <div>Sounds</div>
355
+ </div>
356
+ <div class="bg-dark py-1 rounded">
357
+ <div class="text-primary font-bold">5</div>
358
+ <div>Pads</div>
359
+ </div>
360
+ </div>
361
+ </div>
362
+ `;
363
+ }
364
+
365
+ function renderSoundLibrary() {
366
+ const content = `
367
+ <div class="p-4">
368
+ <div class="flex mb-4">
369
+ <input type="text" placeholder="Search sounds..." class="flex-grow bg-dark text-light p-2 rounded-l-lg border border-secondary focus:outline-none">
370
+ <button class="bg-primary text-dark p-2 rounded-r-lg">
371
+ <i class="fas fa-search"></i>
372
+ </button>
373
+ </div>
374
+
375
+ <div class="mb-6">
376
+ <div class="flex justify-between items-center mb-3">
377
+ <h2 class="medieval-font text-lg text-primary font-bold">All Sounds</h2>
378
+ <button class="text-primary text-sm">
379
+ <i class="fas fa-sort"></i> Sort
380
+ </button>
381
+ </div>
382
+
383
+ <div class="grid grid-cols-2 gap-3">
384
+ ${state.sounds.slice(0, 12).map(sound => renderSoundItem(sound)).join('')}
385
+ </div>
386
+ </div>
387
+
388
+ <div class="mt-6">
389
+ <div class="flex justify-between items-center mb-3">
390
+ <h2 class="medieval-font text-lg text-primary font-bold">Atmosphere</h2>
391
+ </div>
392
+
393
+ <div class="grid grid-cols-3 gap-3">
394
+ ${state.sounds.slice(12, 18).map(sound => renderSoundItem(sound)).join('')}
395
+ </div>
396
+ </div>
397
+ </div>
398
+ `;
399
+
400
+ elements.pageContent.innerHTML = content;
401
+ }
402
+
403
+ function renderSoundItem(sound) {
404
+ const isPlaying = state.playingSounds[sound.id];
405
+ const classes = isPlaying ? 'playing' : '';
406
+ return `
407
+ <div class="sound-item ${classes} bg-secondary rounded-lg p-3 cursor-pointer gradient-border" data-id="${sound.id}">
408
+ <div class="flex justify-between items-start">
409
+ <div class="text-primary text-lg"><i class="fas ${sound.icon}"></i></div>
410
+ <button class="text-light text-xs">
411
+ <i class="fas ${isPlaying ? 'fa-stop' : 'fa-play'}"></i>
412
+ </button>
413
+ </div>
414
+ <div class="mt-2">
415
+ <p class="text-white truncate text-sm">${sound.name}</p>
416
+ <div class="text-light text-xs flex mt-1">
417
+ <span class="bg-dark px-1 rounded mr-1">${sound.category}</span>
418
+ <span class="bg-dark px-1 rounded">${sound.duration}</span>
419
+ </div>
420
+ </div>
421
+ </div>
422
+ `;
423
+ }
424
+
425
+ function renderSoundPad() {
426
+ const content = `
427
+ <div class="p-4">
428
+ <div class="bg-secondary rounded-lg p-3 mb-4 gradient-border">
429
+ <h2 class="medieval-font text-xl text-primary font-bold text-center">Sound Pad</h2>
430
+ <p class="text-center text-light text-sm">Tap any pad to play sounds simultaneously</p>
431
+ </div>
432
+
433
+ <div class="grid grid-cols-4 gap-3">
434
+ ${Array.from({length: 16}, (_, i) => renderPad(i + 1)).join('')}
435
+ </div>
436
+
437
+ <div class="mt-8">
438
+ <div class="flex items-center">
439
+ <span class="text-light mr-2"><i class="fas fa-volume-up text-primary"></i></span>
440
+ <input type="range" min="0" max="1" step="0.01" value="${state.volume}" class="volume-slider flex-grow">
441
+ <span class="text-light ml-2"><i class="fas fa-wave-square text-primary"></i></span>
442
+ </div>
443
+ </div>
444
+
445
+ <div class="flex justify-around mt-8">
446
+ <button class="bg-primary hover:bg-opacity-90 text-dark py-2 px-6 rounded-lg medieval-font font-bold">
447
+ Stop All
448
+ </button>
449
+ <button class="bg-secondary border border-primary text-primary py-2 px-6 rounded-lg medieval-font font-bold">
450
+ Save
451
+ </button>
452
+ </div>
453
+ </div>
454
+ `;
455
+
456
+ elements.pageContent.innerHTML = content;
457
+ }
458
+
459
+ function renderPad(index) {
460
+ const sound = state.sounds.length > index ? state.sounds[index] : null;
461
+ const padContent = sound ? `
462
+ <div class="text-center">
463
+ <div class="text-primary text-lg mb-1"><i class="fas ${sound.icon}"></i></div>
464
+ <div class="text-white text-xs truncate">${sound.name}</div>
465
+ </div>
466
+ ` : `
467
+ <div class="text-center">
468
+ <div class="text-primary text-lg mb-1"><i class="fas fa-plus"></i></div>
469
+ <div class="text-light text-xs">Add Sound</div>
470
+ </div>
471
+ `;
472
+
473
+ return `
474
+ <div class="pad aspect-square bg-secondary rounded-lg flex items-center justify-center gradient-border cursor-pointer">
475
+ ${padContent}
476
+ </div>
477
+ `;
478
+ }
479
+
480
+ function renderPlaylists() {
481
+ const content = `
482
+ <div class="p-4">
483
+ <div class="flex justify-between items-center mb-4">
484
+ <h2 class="medieval-font text-lg text-primary font-bold">Playlists</h2>
485
+ <button class="text-primary">
486
+ <i class="fas fa-plus-circle"></i> New
487
+ </button>
488
+ </div>
489
+
490
+ <div class="grid grid-cols-2 gap-4">
491
+ ${state.playlists.map(playlist => renderPlaylistCard(playlist)).join('')}
492
+ </div>
493
+ </div>
494
+ `;
495
+
496
+ elements.pageContent.innerHTML = content;
497
+ }
498
+
499
+ function renderPlaylistCard(playlist) {
500
+ return `
501
+ <div class="bg-secondary rounded-lg p-3 gradient-border">
502
+ <div class="flex justify-between items-start mb-2">
503
+ <h3 class="text-primary font-bold truncate">${playlist.name}</h3>
504
+ <button class="text-light text-xs">
505
+ <i class="fas fa-play"></i>
506
+ </button>
507
+ </div>
508
+ <div class="text-light text-xs mb-3">${playlist.sounds.length} sounds</div>
509
+ <div class="text-light text-xs">
510
+ <div class="flex overflow-hidden w-full h-6 items-center mb-1">
511
+ ${playlist.sounds.slice(0, 3).map(sound => `
512
+ <div class="rounded-full border border-primary h-4 w-4 flex items-center justify-center mr-1 flex-shrink-0">
513
+ <i class="fas ${sound.icon} text-xs text-primary"></i>
514
+ </div>
515
+ `).join('')}
516
+ </div>
517
+ <div class="text-xs text-light opacity-80">${playlist.tags.join(', ')}</div>
518
+ </div>
519
+ </div>
520
+ `;
521
+ }
522
+
523
+ function renderProjects() {
524
+ const content = `
525
+ <div class="p-4">
526
+ <div class="flex justify-between items-center mb-4">
527
+ <h2 class="medieval-font text-lg text-primary font-bold">Projects</h2>
528
+ <button class="text-primary">
529
+ <i class="fas fa-plus-circle"></i> New
530
+ </button>
531
+ </div>
532
+
533
+ ${state.projects.map(project => renderProjectItem(project)).join('')}
534
+ </div>
535
+ `;
536
+
537
+ elements.pageContent.innerHTML = content;
538
+ }
539
+
540
+ function renderProjectItem(project) {
541
+ const isActive = project.id === (state.currentProject?.id || null);
542
+ const classes = isActive ? 'ring-2 ring-primary' : '';
543
+ return `
544
+ <div class="project-item bg-secondary rounded-lg p-3 mb-3 gradient-border ${classes}">
545
+ <div class="flex justify-between items-center mb-2">
546
+ <h3 class="text-primary font-bold">${project.name}</h3>
547
+ <div>
548
+ <button class="text-light p-1">
549
+ <i class="fas fa-pen"></i>
550
+ </button>
551
+ <button class="text-light p-1">
552
+ <i class="fas fa-ellipsis-v"></i>
553
+ </button>
554
+ </div>
555
+ </div>
556
+ <div class="text-light text-sm mb-3">${project.description}</div>
557
+ <div class="flex justify-between text-xs text-light">
558
+ <div>
559
+ <i class="fas fa-play-circle mr-1 text-primary"></i>
560
+ ${project.playlists.length} playlists
561
+ </div>
562
+ <div>Last edited: ${project.lastEdited}</div>
563
+ </div>
564
+ </div>
565
+ `;
566
+ }
567
+
568
+ function showMainMenu() {
569
+ const menuContent = `
570
+ <div class="fixed inset-0 bg-black bg-opacity-70 flex z-50">
571
+ <div class="bg-secondary w-4/5 max-w-sm h-full p-4 overflow-auto gradient-border">
572
+ <div class="flex justify-between items-center mb-8">
573
+ <h2 class="medieval-font text-xl text-primary">Menu</h2>
574
+ <button id="closeMenuBtn" class="text-primary text-2xl">
575
+ <i class="fas fa-times"></i>
576
+ </button>
577
+ </div>
578
+
579
+ <div class="mb-6">
580
+ <h3 class="medieval-font text-primary text-lg mb-2 font-bold">App Settings</h3>
581
+ <div class="pl-4 text-light">
582
+ <div class="flex items-center py-2">
583
+ <i class="fas fa-volume-up mr-3 text-primary"></i>
584
+ <div class="flex-grow">Volume</div>
585
+ <div class="w-1/3">
586
+ <input type="range" min="0" max="1" step="0.01" value="0.7" class="volume-slider w-full">
587
+ </div>
588
+ </div>
589
+ <div class="flex items-center py-2">
590
+ <i class="fas fa-music mr-3 text-primary"></i>
591
+ <div class="flex-grow">Auto-play</div>
592
+ <label class="relative inline-block w-10 h-6">
593
+ <input type="checkbox" class="opacity-0 w-0 h-0 peer">
594
+ <span class="absolute cursor-pointer top-0 left-0 right-0 bottom-0 bg-dark rounded-full transition peer-checked:bg-primary"></span>
595
+ <span class="absolute h-4 w-4 bg-white rounded-full left-1 top-1 transition peer-checked:translate-x-4"></span>
596
+ </label>
597
+ </div>
598
+ </div>
599
+ </div>
600
+
601
+ <div class="mb-6">
602
+ <h3 class="medieval-font text-primary text-lg mb-2 font-bold">Import/Export</h3>
603
+ <div class="grid grid-cols-2 gap-2">
604
+ <button class="py-2 text-primary border border-primary rounded-md text-center">
605
+ <i class="fas fa-download mr-1"></i> Import
606
+ </button>
607
+ <button class="py-2 bg-primary text-dark rounded-md text-center">
608
+ <i class="fas fa-upload mr-1"></i> Export
609
+ </button>
610
+ </div>
611
+ </div>
612
+
613
+ <div>
614
+ <h3 class="medieval-font text-primary text-lg mb-2 font-bold">Pre-made Packs</h3>
615
+ <div class="flex overflow-x-auto pb-2 -mx-2 px-2">
616
+ <div class="flex-shrink-0 w-32 mr-3">
617
+ <div class="bg-dark aspect-video rounded flex items-center justify-center">
618
+ <i class="fas fa-dragon text-2xl text-primary"></i>
619
+ </div>
620
+ <div class="text-light text-xs text-center mt-1">Monsters</div>
621
+ </div>
622
+ <div class="flex-shrink-0 w-32 mr-3">
623
+ <div class="bg-dark aspect-video rounded flex items-center justify-center">
624
+ <i class="fas fa-city text-2xl text-primary"></i>
625
+ </div>
626
+ <div class="text-light text-xs text-center mt-1">Cities</div>
627
+ </div>
628
+ <div class="flex-shrink-0 w-32 mr-3">
629
+ <div class="bg-dark aspect-video rounded flex items-center justify-center">
630
+ <i class="fas fa-water text-2xl text-primary"></i>
631
+ </div>
632
+ <div class="text-light text-xs text-center mt-1">Nature</div>
633
+ </div>
634
+ </div>
635
+ </div>
636
+ </div>
637
+ </div>
638
+ `;
639
+
640
+ const menuContainer = document.createElement('div');
641
+ menuContainer.innerHTML = menuContent;
642
+ document.body.appendChild(menuContainer);
643
+
644
+ menuContainer.querySelector('#closeMenuBtn').addEventListener('click', () => {
645
+ menuContainer.remove();
646
+ });
647
+ }
648
+
649
+ // Helper Functions
650
+
651
+ function loadState() {
652
+ // Load from localStorage or localForage
653
+ const savedState = localStorage.getItem('dmsoundscapes-state');
654
+ if (savedState) {
655
+ Object.assign(state, JSON.parse(savedState));
656
+ }
657
+ }
658
+
659
+ function saveState() {
660
+ localStorage.setItem('dmsoundscapes-state', JSON.stringify(state));
661
+ }
662
+
663
+ function setupSampleData() {
664
+ if (state.sounds.length === 0) {
665
+ state.sounds = [
666
+ { id: 1, name: "Medieval Tavern", icon: "fa-beer", category: "Ambience", duration: "4:20" },
667
+ { id: 2, name: "Castle Hall", icon: "fa-landmark", category: "Ambience", duration: "3:45" },
668
+ { id: 3, name: "Forest Night", icon: "fa-tree", category: "Ambience", duration: "5:15" },
669
+ { id: 4, name: "Sword Clash", icon: "fa-swords", category: "Combat", duration: "0:07" },
670
+ { id: 5, name: "Fireball", icon: "fa-fire", category: "Spells", duration: "0:12" },
671
+ { id: 6, name: "Orc Roar", icon: "fa-dragon", category: "Monsters", duration: "0:08" },
672
+ { id: 7, name: "Rainstorm", icon: "fa-cloud-rain", category: "Weather", duration: "6:00" },
673
+ { id: 8, name: "Horse Galloping", icon: "fa-horse", category: "Travel", duration: "0:45" },
674
+ { id: 9, name: "Crowd Cheer", icon: "fa-users", category: "Ambience", duration: "0:15" },
675
+ { id: 10, name: "Creaky Door", icon: "fa-door-open", category: "Effects", duration: "0:05" },
676
+ { id: 11, name: "Dragon Roar", icon: "fa-dragon", category: "Monsters", duration: "0:18" },
677
+ { id: 12, name: "Magic Portal", icon: "fa-portal-enter", category: "Spells", duration: "0:20" }
678
+ ];
679
+ }
680
+
681
+ if (state.playlists.length === 0) {
682
+ state.playlists = [
683
+ {
684
+ id: 1,
685
+ name: "Castle Siege",
686
+ sounds: [state.sounds[2], state.sounds[3], state.sounds[5], state.sounds[10]],
687
+ tags: ["combat", "dramatic"],
688
+ lastEdited: "Yesterday"
689
+ },
690
+ {
691
+ id: 2,
692
+ name: "Peaceful Village",
693
+ sounds: [state.sounds[0], state.sounds[1], state.sounds[8], state.sounds[11]],
694
+ tags: ["ambience", "calm"],
695
+ lastEdited: "2 days ago"
696
+ }
697
+ ];
698
+ }
699
+
700
+ if (state.projects.length === 0) {
701
+ state.projects = [
702
+ {
703
+ id: 1,
704
+ name: "Lost Mines Campaign",
705
+ description: "Main campaign sound setup",
706
+ playlists: [state.playlists[0], state.playlists[1]],
707
+ lastEdited: "Today"
708
+ },
709
+ {
710
+ id: 2,
711
+ name: "Icewind Dale",
712
+ description: "Winter campaign sounds",
713
+ playlists: [state.playlists[1]],
714
+ lastEdited: "1 week ago"
715
+ }
716
+ ];
717
+ state.currentProject = state.projects[0];
718
+ }
719
+
720
+ saveState();
721
+ }
722
+
723
+ function countProjectSounds(project) {
724
+ return project.playlists.reduce((acc, playlist) => acc + playlist.sounds.length, 0);
725
+ }
726
+
727
+ // Initialize app when DOM is loaded
728
+ document.addEventListener('DOMContentLoaded', initApp);
729
+ </script>
730
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ZoroaStrella/rawdio" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
731
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ Create a mobile-ready, responsive app designed primarily for tablets and phones, with a UI to help dungeon masters select and play sounds together to create immersive atmospheres during gameplay. Use local forage, consider that we will run this in webview or progressive webapp, consider options for those persistence Users can: - Create projects and playlists, pads - a project may have many playlist, a pad is a collection of songs and sounds, noises that can play together, simultaneously or played as clicked with a UI - Search for projects, songs, videos, sounds, or playlists. - Add, delete, and organize sounds, videos, or songs in projects and playlists. - Handle YouTube, SoundCloud, Vimeo, and Dailymotion URLs for external audio and video sources. - Support multiple sound formats like m4a, wav, mp3, mp4, and playlists. - Arrange sounds and scroll through project or playlist items. - Play multiple sounds, songs, or videos simultaneously. - Use a pad-like interface to load sounds, videos, or songs and edit settings, such as selecting sound segments like a live sampler like a daw workstation such as ableton wit hminimalism and ux in mind. - Create sections within playlists for easy navigation. - Include basic atmospheric sounds and noise generators. - Automatically or manually tag and categorize sounds, videos, and songs for easy searching. - Handle transition properties in advance or live mode. This app enables dungeon masters to craft sonic soundscapes, access fast sounds, or use pre-prepared playlists while optimizing background processes for smooth performance on phones. It allows simultaneous playback and searching of playlists and sounds without restrictions. Optimize data loading and buffering, and provide basic sound editing with signal processing features. Use the best available TypeScript audio library and React for development. The design should feature gold and purple colors with medieval fonts. Include specific tags for various game sounds and background music, such as OSTs and original soundtracks for films and games. Add pre-built TTRPG, D&D playlists, draw inspiration from existing products with similar features, and prioritize user experience with bottom navigation and action buttons.