pk
Browse files- index.html +270 -189
index.html
CHANGED
@@ -1,227 +1,308 @@
|
|
1 |
<!DOCTYPE html>
|
2 |
-
<html>
|
3 |
<head>
|
4 |
-
<meta charset="
|
5 |
-
<meta name="viewport" content="width=device-width"
|
6 |
-
<title>
|
7 |
<style>
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
}
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
border:
|
21 |
-
|
22 |
-
|
23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
}
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
28 |
color: white;
|
|
|
|
|
|
|
|
|
29 |
}
|
30 |
-
|
31 |
-
|
32 |
-
background-color: #
|
33 |
}
|
34 |
-
|
35 |
-
|
36 |
-
margin-top:
|
37 |
-
|
|
|
|
|
38 |
}
|
39 |
|
40 |
-
.
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
border-radius: 5px;
|
45 |
}
|
46 |
</style>
|
47 |
</head>
|
48 |
<body>
|
49 |
-
<div
|
50 |
-
<h1>
|
51 |
-
<
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
54 |
</div>
|
55 |
|
56 |
<script>
|
57 |
-
class
|
58 |
constructor() {
|
59 |
-
this.
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
this.responseDiv = document.getElementById('response');
|
65 |
-
|
66 |
-
this.recordButton.addEventListener('click', () => this.toggleRecording());
|
67 |
|
68 |
-
|
69 |
-
this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
});
|
85 |
-
|
86 |
-
this.mediaRecorder.ondataavailable = (event) => {
|
87 |
-
this.audioChunks.push(event.data);
|
88 |
-
};
|
89 |
|
90 |
-
|
91 |
-
|
92 |
-
await this.convertAndSend(audioBlob);
|
93 |
-
this.audioChunks = [];
|
94 |
-
};
|
95 |
|
96 |
-
|
97 |
-
console.error('Error accessing microphone:', error);
|
98 |
-
this.statusDiv.textContent = 'Error accessing microphone. Please ensure microphone permissions are granted.';
|
99 |
}
|
100 |
}
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
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 |
-
|
131 |
-
|
132 |
-
|
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 |
-
|
149 |
-
const
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
|
154 |
-
const
|
155 |
-
|
156 |
-
const view = new DataView(buffer);
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
201 |
},
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
}
|
218 |
-
}
|
219 |
}
|
220 |
|
221 |
-
//
|
222 |
-
|
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>
|