diff --git a/workspaces/simon-game/src/app/components/App.tsx b/workspaces/simon-game/src/app/components/App.tsx
index 895d9015..4a5c06dc 100644
--- a/workspaces/simon-game/src/app/components/App.tsx
+++ b/workspaces/simon-game/src/app/components/App.tsx
@@ -1,19 +1,15 @@
import React from "react";
import { Box } from "./Box.tsx";
+import { playNote } from "../util/playNote.ts";
+import { config } from "../constants.ts";
export function App() {
return (
-
-
-
-
-
+
+ {config.boxes.map((box) => (
+ playNote(box.frequency)} />
+ ))}
);
}
diff --git a/workspaces/simon-game/src/app/components/Box.tsx b/workspaces/simon-game/src/app/components/Box.tsx
index 41032b28..64e989ab 100644
--- a/workspaces/simon-game/src/app/components/Box.tsx
+++ b/workspaces/simon-game/src/app/components/Box.tsx
@@ -5,18 +5,28 @@ type Props = {
height?: number;
width?: number;
margin?: number;
+ onClick: () => void;
};
-export function Box(props: Props) {
+export function Box({
+ color = "lightblue",
+ height = 100,
+ width = 100,
+ margin,
+ onClick,
+}: Props) {
return (
-
);
}
diff --git a/workspaces/simon-game/src/app/constants.ts b/workspaces/simon-game/src/app/constants.ts
new file mode 100644
index 00000000..59317da3
--- /dev/null
+++ b/workspaces/simon-game/src/app/constants.ts
@@ -0,0 +1,22 @@
+export const config = {
+ soundDurationMs: 300,
+ volumePct: 0.1,
+ boxes: [
+ {
+ color: "red",
+ frequency: 261.63,
+ },
+ {
+ color: "#0050B5", // cobalt blue
+ frequency: 329.63,
+ },
+ {
+ color: "green",
+ frequency: 392.0,
+ },
+ {
+ color: "yellow",
+ frequency: 523.25,
+ },
+ ],
+};
diff --git a/workspaces/simon-game/src/app/util/playNote.ts b/workspaces/simon-game/src/app/util/playNote.ts
new file mode 100644
index 00000000..f33e716a
--- /dev/null
+++ b/workspaces/simon-game/src/app/util/playNote.ts
@@ -0,0 +1,6 @@
+import { config } from "../constants.ts";
+import { playSound } from "./playSound.ts";
+
+export function playNote(freq: number): void {
+ playSound(freq, config.soundDurationMs, config.volumePct);
+}
diff --git a/workspaces/simon-game/src/app/util/playSound.ts b/workspaces/simon-game/src/app/util/playSound.ts
new file mode 100644
index 00000000..5c37e4be
--- /dev/null
+++ b/workspaces/simon-game/src/app/util/playSound.ts
@@ -0,0 +1,55 @@
+import { MS_IN_SEC } from "@code-chronicles/util/timeConstants";
+
+let audioContext: AudioContext | null = null;
+
+const FADE_DURATION = 0.01;
+
+/**
+ * Plays a sound using the Web Audio API.
+ *
+ * @param context - The audio context to use for generating the sound.
+ * @param frequency - The frequency of the sound in Hertz
+ * @param durationMs - The duration of the sound in milliseconds.
+ * @param volumePct - The volume of the sound, typically between 0 and 1.
+ */
+export function playSound(
+ frequency: number,
+ durationMs: number,
+ volumePct: number,
+): void {
+ if (frequency <= 0) {
+ throw new RangeError("Frequency must be a positive number.");
+ }
+
+ if (durationMs <= 0) {
+ throw new RangeError("Duration must be a positive number.");
+ }
+
+ if (volumePct < 0 || volumePct > 1) {
+ throw new RangeError("Volume must be between 0 and 1.");
+ }
+
+ audioContext ??= new AudioContext();
+
+ const oscillatorNode = audioContext.createOscillator();
+ oscillatorNode.frequency.setValueAtTime(frequency, audioContext.currentTime);
+
+ const gainNode = audioContext.createGain();
+ gainNode.gain.setValueAtTime(volumePct, audioContext.currentTime);
+
+ // Creates the smooth fades-in / fade-out sound effect (Avoids the popping sounds)
+ gainNode.gain.linearRampToValueAtTime(
+ volumePct,
+ audioContext.currentTime + FADE_DURATION,
+ );
+ gainNode.gain.linearRampToValueAtTime(
+ 0,
+ audioContext.currentTime + durationMs / MS_IN_SEC - FADE_DURATION,
+ );
+
+ oscillatorNode.connect(gainNode);
+ gainNode.connect(audioContext.destination);
+
+ oscillatorNode.start();
+ oscillatorNode.stop(audioContext.currentTime + durationMs / MS_IN_SEC);
+}