zshashz commited on
Commit
a16d218
·
1 Parent(s): aea2245
Files changed (1) hide show
  1. index.html +270 -189
index.html CHANGED
@@ -1,227 +1,308 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>Voice Recorder</title>
7
  <style>
8
- .card {
9
- max-width: 600px;
10
- margin: 2rem auto;
11
- padding: 2rem;
12
- border-radius: 10px;
13
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
14
- text-align: center;
15
  }
16
-
17
- .button {
18
- padding: 1rem 2rem;
19
- margin: 0.5rem;
20
- border: none;
21
- border-radius: 5px;
22
- cursor: pointer;
23
- font-size: 1rem;
 
 
 
 
 
 
 
 
 
 
24
  }
25
-
26
- .record {
27
- background-color: #ff4444;
 
28
  color: white;
 
 
 
 
29
  }
30
-
31
- .record.recording {
32
- background-color: #aa0000;
33
  }
34
-
35
- .status {
36
- margin-top: 1rem;
37
- font-style: italic;
 
 
38
  }
39
 
40
- .response {
41
- margin-top: 1rem;
42
- padding: 1rem;
43
- background-color: #f5f5f5;
44
- border-radius: 5px;
45
  }
46
  </style>
47
  </head>
48
  <body>
49
- <div class="card">
50
- <h1>Voice Recorder</h1>
51
- <button id="recordButton" class="button record">Start Recording</button>
52
- <div id="status" class="status">Click to start recording</div>
53
- <div id="response" class="response"></div>
 
 
 
 
54
  </div>
55
 
56
  <script>
57
- class VoiceRecorder {
58
  constructor() {
59
- this.mediaRecorder = null;
60
- this.audioChunks = [];
61
- this.isRecording = false;
62
- this.recordButton = document.getElementById('recordButton');
63
- this.statusDiv = document.getElementById('status');
64
- this.responseDiv = document.getElementById('response');
65
-
66
- this.recordButton.addEventListener('click', () => this.toggleRecording());
67
 
68
- // Replace with your Eleven Labs API key
69
- this.ELEVEN_LABS_API_KEY = 'sk_f54a899fc3dd71448fc4a1e2c07f15aafa93580cdc50d853';
 
 
 
 
 
 
 
 
70
  }
71
-
72
- async setupMediaRecorder() {
73
- try {
74
- const stream = await navigator.mediaDevices.getUserMedia({
75
- audio: {
76
- sampleRate: 16000,
77
- channelCount: 1
78
- }
79
- });
80
-
81
- this.mediaRecorder = new MediaRecorder(stream, {
82
- mimeType: 'audio/webm',
83
- audioBitsPerSecond: 128000
84
- });
85
-
86
- this.mediaRecorder.ondataavailable = (event) => {
87
- this.audioChunks.push(event.data);
88
- };
89
 
90
- this.mediaRecorder.onstop = async () => {
91
- const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });
92
- await this.convertAndSend(audioBlob);
93
- this.audioChunks = [];
94
- };
95
 
96
- } catch (error) {
97
- console.error('Error accessing microphone:', error);
98
- this.statusDiv.textContent = 'Error accessing microphone. Please ensure microphone permissions are granted.';
99
  }
100
  }
101
-
102
- async toggleRecording() {
103
- if (!this.mediaRecorder) {
104
- await this.setupMediaRecorder();
105
- }
106
-
107
- if (!this.isRecording) {
108
- this.startRecording();
109
- } else {
110
- this.stopRecording();
111
- }
112
- }
113
-
114
- startRecording() {
115
- this.mediaRecorder.start();
116
- this.isRecording = true;
117
- this.recordButton.textContent = 'Stop Recording';
118
- this.recordButton.classList.add('recording');
119
- this.statusDiv.textContent = 'Recording...';
120
- }
121
-
122
- stopRecording() {
123
- this.mediaRecorder.stop();
124
- this.isRecording = false;
125
- this.recordButton.textContent = 'Start Recording';
126
- this.recordButton.classList.remove('recording');
127
- this.statusDiv.textContent = 'Processing audio...';
128
  }
129
 
130
- async convertAndSend(audioBlob) {
131
- try {
132
- // Convert to audio buffer
133
- const arrayBuffer = await audioBlob.arrayBuffer();
134
- const audioContext = new AudioContext();
135
- const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
136
-
137
- // Create WAV file
138
- const wavBuffer = this.audioBufferToWav(audioBuffer);
139
- const wavBlob = new Blob([wavBuffer], { type: 'audio/wav' });
140
-
141
- await this.sendToElevenLabs(wavBlob);
142
- } catch (error) {
143
- console.error('Error converting audio:', error);
144
- this.statusDiv.textContent = 'Error converting audio. Please try again.';
145
- }
146
  }
147
-
148
- audioBufferToWav(audioBuffer) {
149
- const numberOfChannels = 1; // Mono
150
- const sampleRate = 16000;
151
- const format = 1; // PCM
152
- const bitDepth = 16;
153
 
154
- const length = audioBuffer.length * numberOfChannels * (bitDepth / 8);
155
- const buffer = new ArrayBuffer(44 + length);
156
- const view = new DataView(buffer);
157
 
158
- // Write WAV header
159
- const writeString = (view, offset, string) => {
160
- for (let i = 0; i < string.length; i++) {
161
- view.setUint8(offset + i, string.charCodeAt(i));
 
 
 
 
 
 
 
 
162
  }
163
- };
164
-
165
- writeString(view, 0, 'RIFF');
166
- view.setUint32(4, 36 + length, true);
167
- writeString(view, 8, 'WAVE');
168
- writeString(view, 12, 'fmt ');
169
- view.setUint32(16, 16, true);
170
- view.setUint16(20, format, true);
171
- view.setUint16(22, numberOfChannels, true);
172
- view.setUint32(24, sampleRate, true);
173
- view.setUint32(28, sampleRate * numberOfChannels * (bitDepth / 8), true);
174
- view.setUint16(32, numberOfChannels * (bitDepth / 8), true);
175
- view.setUint16(34, bitDepth, true);
176
- writeString(view, 36, 'data');
177
- view.setUint32(40, length, true);
178
-
179
- // Write audio data
180
- const channelData = audioBuffer.getChannelData(0);
181
- let offset = 44;
182
- for (let i = 0; i < channelData.length; i++) {
183
- const sample = Math.max(-1, Math.min(1, channelData[i]));
184
- view.setInt16(offset, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
185
- offset += 2;
186
- }
187
-
188
- return buffer;
189
  }
190
-
191
- async sendToElevenLabs(wavBlob) {
192
- try {
193
- const formData = new FormData();
194
- formData.append('file', wavBlob, 'recording.wav');
195
-
196
- const response = await fetch('https://api.elevenlabs.io/v2/speech-recognition', {
197
- method: 'POST',
198
- headers: {
199
- 'xi-api-key': this.ELEVEN_LABS_API_KEY,
200
- 'Accept': 'application/json'
 
 
201
  },
202
- body: formData
203
- });
204
-
205
- if (!response.ok) {
206
- const errorText = await response.text();
207
- throw new Error(`HTTP error! status: ${response.status}, ${errorText}`);
208
- }
209
-
210
- const data = await response.json();
211
- this.responseDiv.textContent = `Transcription: ${data.text}`;
212
- this.statusDiv.textContent = 'Transcription complete';
213
-
214
- } catch (error) {
215
- console.error('Error sending to Eleven Labs:', error);
216
- this.statusDiv.textContent = 'Error sending to Eleven Labs API. Please check your API key and try again.';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
- }
219
  }
220
 
221
- // Initialize the voice recorder when the page loads
222
- window.addEventListener('load', () => {
223
- new VoiceRecorder();
224
- });
225
  </script>
226
  </body>
227
  </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>Shy Guy Simulator</title>
7
  <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ max-width: 800px;
11
+ margin: 20px auto;
12
+ padding: 20px;
13
+ background-color: #1a1a1a;
14
+ color: #fff;
15
  }
16
+
17
+ #game-container {
18
+ background-color: #2a2a2a;
19
+ padding: 20px;
20
+ border-radius: 8px;
21
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
22
+ }
23
+
24
+ #story-text {
25
+ margin-bottom: 20px;
26
+ line-height: 1.6;
27
+ min-height: 100px;
28
+ }
29
+
30
+ #choices {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 10px;
34
  }
35
+
36
+ button {
37
+ padding: 10px 20px;
38
+ background-color: #4a4a4a;
39
  color: white;
40
+ border: none;
41
+ border-radius: 4px;
42
+ cursor: pointer;
43
+ transition: background-color 0.3s;
44
  }
45
+
46
+ button:hover {
47
+ background-color: #666;
48
  }
49
+
50
+ #stats {
51
+ margin-top: 20px;
52
+ padding: 10px;
53
+ background-color: #333;
54
+ border-radius: 4px;
55
  }
56
 
57
+ .thought-bubble {
58
+ font-style: italic;
59
+ color: #aaa;
60
+ margin: 10px 0;
 
61
  }
62
  </style>
63
  </head>
64
  <body>
65
+ <div id="game-container">
66
+ <h1>Shy Guy Simulator</h1>
67
+ <div id="stats">
68
+ Confidence: <span id="confidence">0</span>% |
69
+ Drinks: <span id="drinks">0</span> |
70
+ Time: <span id="time">8:00 PM</span>
71
+ </div>
72
+ <div id="story-text"></div>
73
+ <div id="choices"></div>
74
  </div>
75
 
76
  <script>
77
+ class ShyGuyGame {
78
  constructor() {
79
+ this.stats = {
80
+ confidence: 0,
81
+ drinks: 0,
82
+ time: new Date(2024, 0, 1, 20, 0) // 8:00 PM
83
+ };
 
 
 
84
 
85
+ this.currentScene = 'start';
86
+ this.thoughts = [
87
+ "Maybe she won't like me...",
88
+ "What if I say something stupid?",
89
+ "Everyone's watching...",
90
+ "I should probably just go home...",
91
+ "She looks way out of my league..."
92
+ ];
93
+
94
+ this.initialize();
95
  }
96
+
97
+ initialize() {
98
+ this.updateStats();
99
+ this.loadScene(this.currentScene);
100
+ setInterval(() => this.showRandomThought(), 15000);
101
+ }
102
+
103
+ showRandomThought() {
104
+ if (this.stats.confidence < 70) {
105
+ const thoughtText = this.thoughts[Math.floor(Math.random() * this.thoughts.length)];
106
+ const thoughtBubble = document.createElement('div');
107
+ thoughtBubble.className = 'thought-bubble';
108
+ thoughtBubble.textContent = `Inner voice: "${thoughtText}"`;
 
 
 
 
 
109
 
110
+ const storyText = document.getElementById('story-text');
111
+ storyText.insertBefore(thoughtBubble, storyText.firstChild);
 
 
 
112
 
113
+ setTimeout(() => thoughtBubble.remove(), 5000);
 
 
114
  }
115
  }
116
+
117
+ updateStats() {
118
+ document.getElementById('confidence').textContent = this.stats.confidence;
119
+ document.getElementById('drinks').textContent = this.stats.drinks;
120
+ document.getElementById('time').textContent =
121
+ this.stats.time.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
+ advanceTime(minutes) {
125
+ this.stats.time = new Date(this.stats.time.getTime() + minutes * 60000);
126
+ this.updateStats();
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
128
+
129
+ loadScene(sceneId) {
130
+ const scene = this.scenes[sceneId];
131
+ if (!scene) return;
132
+
133
+ document.getElementById('story-text').innerHTML = scene.text;
134
 
135
+ const choicesContainer = document.getElementById('choices');
136
+ choicesContainer.innerHTML = '';
 
137
 
138
+ scene.choices.forEach(choice => {
139
+ if (!choice.condition || choice.condition(this.stats)) {
140
+ const button = document.createElement('button');
141
+ button.textContent = choice.text;
142
+ button.onclick = () => {
143
+ if (choice.effect) {
144
+ choice.effect(this.stats);
145
+ this.updateStats();
146
+ }
147
+ this.loadScene(choice.next);
148
+ };
149
+ choicesContainer.appendChild(button);
150
  }
151
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
+
154
+ scenes = {
155
+ start: {
156
+ text: "You're at the homecoming party, standing awkwardly by the snack table. Across the room, you see her - the pretty girl you've been too nervous to talk to all semester. What do you do?",
157
+ choices: [
158
+ {
159
+ text: "Get a drink to calm your nerves",
160
+ effect: stats => {
161
+ stats.drinks++;
162
+ stats.confidence += 10;
163
+ this.advanceTime(15);
164
+ },
165
+ next: 'after_drink'
166
  },
167
+ {
168
+ text: "Try to move closer to the dance floor",
169
+ effect: stats => {
170
+ stats.confidence += 5;
171
+ this.advanceTime(10);
172
+ },
173
+ next: 'dance_floor'
174
+ },
175
+ {
176
+ text: "Stay by the snacks and observe",
177
+ effect: stats => {
178
+ this.advanceTime(20);
179
+ },
180
+ next: 'observe'
181
+ }
182
+ ]
183
+ },
184
+ after_drink: {
185
+ text: "The drink starts to take effect, making you feel a bit warmer and more relaxed. The music seems more inviting now.",
186
+ choices: [
187
+ {
188
+ text: "Get another drink",
189
+ effect: stats => {
190
+ stats.drinks++;
191
+ stats.confidence += 15;
192
+ this.advanceTime(15);
193
+ },
194
+ next: 'more_drinks'
195
+ },
196
+ {
197
+ text: "Start swaying to the music",
198
+ effect: stats => {
199
+ stats.confidence += 10;
200
+ this.advanceTime(10);
201
+ },
202
+ next: 'dancing'
203
+ }
204
+ ]
205
+ },
206
+ more_drinks: {
207
+ text: "You're definitely feeling the drinks now. Your anxiety is lower, but you're also a bit wobbly.",
208
+ choices: [
209
+ {
210
+ text: "Try to approach her",
211
+ condition: stats => stats.confidence >= 40,
212
+ effect: stats => this.advanceTime(5),
213
+ next: 'approach'
214
+ },
215
+ {
216
+ text: "Take a break outside",
217
+ effect: stats => {
218
+ stats.confidence -= 10;
219
+ this.advanceTime(20);
220
+ },
221
+ next: 'outside'
222
+ }
223
+ ]
224
+ },
225
+ dance_floor: {
226
+ text: "You're closer to where she's standing now. The music is louder, and people are dancing around you.",
227
+ choices: [
228
+ {
229
+ text: "Start dancing near her group",
230
+ effect: stats => {
231
+ stats.confidence += 15;
232
+ this.advanceTime(15);
233
+ },
234
+ next: 'dancing'
235
+ },
236
+ {
237
+ text: "Retreat to get a drink",
238
+ effect: stats => {
239
+ stats.drinks++;
240
+ stats.confidence += 5;
241
+ this.advanceTime(10);
242
+ },
243
+ next: 'after_drink'
244
+ }
245
+ ]
246
+ },
247
+ dancing: {
248
+ text: "You're dancing! Not too badly either. You notice she's glancing in your direction occasionally.",
249
+ choices: [
250
+ {
251
+ text: "Make eye contact and smile",
252
+ effect: stats => {
253
+ stats.confidence += 20;
254
+ this.advanceTime(5);
255
+ },
256
+ next: 'eye_contact'
257
+ },
258
+ {
259
+ text: "Keep dancing but avoid eye contact",
260
+ effect: stats => {
261
+ stats.confidence += 5;
262
+ this.advanceTime(15);
263
+ },
264
+ next: 'continue_dancing'
265
+ }
266
+ ]
267
+ },
268
+ eye_contact: {
269
+ text: "She smiles back! Your heart is racing, but in a good way.",
270
+ choices: [
271
+ {
272
+ text: "Walk over and say hi",
273
+ condition: stats => stats.confidence >= 60,
274
+ next: 'success'
275
+ },
276
+ {
277
+ text: "Panic and get another drink",
278
+ effect: stats => {
279
+ stats.drinks++;
280
+ stats.confidence += 10;
281
+ this.advanceTime(10);
282
+ },
283
+ next: 'more_drinks'
284
+ }
285
+ ]
286
+ },
287
+ success: {
288
+ text: "Congratulations! You managed to overcome your shyness and talk to her. Turns out, she's been hoping you'd come say hi all evening. Sometimes the hardest part is just taking that first step.",
289
+ choices: [
290
+ {
291
+ text: "Play Again",
292
+ effect: stats => {
293
+ stats.confidence = 0;
294
+ stats.drinks = 0;
295
+ stats.time = new Date(2024, 0, 1, 20, 0);
296
+ },
297
+ next: 'start'
298
+ }
299
+ ]
300
  }
301
+ };
302
  }
303
 
304
+ // Start the game
305
+ const game = new ShyGuyGame();
 
 
306
  </script>
307
  </body>
308
  </html>