diff --git a/MusicPlayer/cobie1818/README.md b/MusicPlayer/cobie1818/README.md
new file mode 100644
index 000000000..60a433aa6
--- /dev/null
+++ b/MusicPlayer/cobie1818/README.md
@@ -0,0 +1,64 @@
+# SoundSpace Music Player
+
+A responsive music player built for JavaScript Mini Projects.
+Contribution related to issue #958.
+
+## Technologies
+
+- HTML5 audio
+- CSS with a responsive layout
+- Vanilla JavaScript
+- No external libraries, API keys, or build tools required
+
+## Run locally
+
+Open index.html in a modern browser, or serve this folder using
+the VS Code Live Server extension.
+
+## How to use
+
+1. Select Add audio files and choose audio files from your device.
+2. Use Ctrl or Shift in the file picker to select multiple files.
+3. Click Play to begin playback.
+4. Use Previous, Next, or a playlist button to select a track.
+5. Adjust the track position and volume using the sliders.
+
+Adding a new selection replaces the playlist.
+Files are processed locally and are not uploaded.
+The playlist is cleared when the page reloads.
+
+## Features
+
+- Play and pause
+- Previous and next track navigation with wraparound
+- Automatic advancement until the end of the playlist
+- Clickable playlist with the selected track highlighted
+- Track seeking and elapsed time display
+- Volume control
+- Responsive layout
+- Keyboard-operable buttons and labeled controls
+- Visible keyboard focus and playback status messages
+- Error feedback for files the browser cannot play
+
+## Manual testing checklist
+
+- Add one file and test play, pause, seeking, and volume.
+- Add multiple files and test previous, next, and track selection.
+- Confirm navigation wraps around at the playlist boundaries.
+- Confirm playback advances when a track ends.
+- Confirm playback stops after the final track.
+- Replace the playlist while music is playing.
+- Try an unsupported or damaged audio file.
+- Test keyboard navigation using Tab, Enter, Space, and arrow keys.
+- Check the layout at phone, tablet, and desktop widths.
+- Check the browser console for unexpected errors.
+
+## Limitations
+
+Supported audio formats depend on the browser.
+Audio files are supplied by the user; no songs are bundled.
+No automated test suite is included in this contribution.
+
+## Screenshot
+
+
\ No newline at end of file
diff --git a/MusicPlayer/cobie1818/index.html b/MusicPlayer/cobie1818/index.html
new file mode 100644
index 000000000..8ae7dee2d
--- /dev/null
+++ b/MusicPlayer/cobie1818/index.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+ SoundSpace Music Player
+
+
+
+
+
+
+
+
+ ♫
+ No track selected
+ Choose audio files to get started.
+
+ Add audio files
+
+ Files stay on your device. Adding files replaces the playlist.
+
+
+
+
+
Track position
+
+
+ 0:00
+ 0:00
+
+
+
+
+ Previous
+ Play
+ Next
+
+
+
+ Volume
+
+
+
+
+
+ Your playlist
+ Your added tracks will appear here.
+
+
+
+
+
\ No newline at end of file
diff --git a/MusicPlayer/cobie1818/screenshot.png b/MusicPlayer/cobie1818/screenshot.png
new file mode 100644
index 000000000..85a37a3ef
Binary files /dev/null and b/MusicPlayer/cobie1818/screenshot.png differ
diff --git a/MusicPlayer/cobie1818/script.js b/MusicPlayer/cobie1818/script.js
new file mode 100644
index 000000000..407de7814
--- /dev/null
+++ b/MusicPlayer/cobie1818/script.js
@@ -0,0 +1,194 @@
+"use strict";
+
+const audio = document.getElementById("audio");
+const filesInput = document.getElementById("files");
+const title = document.getElementById("now-playing");
+const status = document.getElementById("status");
+const playButton = document.getElementById("play");
+const previousButton = document.getElementById("previous");
+const nextButton = document.getElementById("next");
+const seek = document.getElementById("seek");
+const volume = document.getElementById("volume");
+const elapsed = document.getElementById("elapsed");
+const duration = document.getElementById("duration");
+const playlist = document.getElementById("playlist");
+const empty = document.getElementById("empty");
+
+let tracks = [];
+let currentIndex = -1;
+let playbackRequest = 0;
+
+audio.volume = Number(volume.value);
+
+function formatTime(seconds) {
+ if (!Number.isFinite(seconds) || seconds < 0) {
+ return "0:00";
+ }
+
+ const minutes = Math.floor(seconds / 60);
+ const remainder = Math.floor(seconds % 60);
+ return `${minutes}:${String(remainder).padStart(2, "0")}`;
+}
+
+function updateTimeline() {
+ const hasDuration = Number.isFinite(audio.duration)
+ && audio.duration > 0;
+
+ seek.disabled = !hasDuration;
+ seek.max = hasDuration ? audio.duration : 100;
+ seek.value = hasDuration ? audio.currentTime : 0;
+ elapsed.textContent = formatTime(audio.currentTime);
+ duration.textContent = formatTime(audio.duration);
+ seek.setAttribute(
+ "aria-valuetext",
+ `${formatTime(audio.currentTime)} of ${formatTime(audio.duration)}`
+ );
+}
+
+function highlightTrack() {
+ const buttons = playlist.querySelectorAll("button");
+
+ buttons.forEach((button, index) => {
+ if (index === currentIndex) {
+ button.setAttribute("aria-current", "true");
+ } else {
+ button.removeAttribute("aria-current");
+ }
+ });
+}
+
+async function startPlayback() {
+ if (currentIndex < 0) return;
+
+ const request = ++playbackRequest;
+
+ try {
+ await audio.play();
+ } catch (error) {
+ // Ignore a request interrupted by changing or pausing a track.
+ if (request !== playbackRequest || error.name === "AbortError") {
+ return;
+ }
+
+ status.textContent =
+ "Unable to play this file. Try another audio file.";
+ }
+}
+
+function selectTrack(index, autoplay = false) {
+ if (!tracks.length) return;
+
+ playbackRequest++;
+ audio.pause();
+
+ // Wrap around when moving beyond either end of the playlist.
+ currentIndex = (index + tracks.length) % tracks.length;
+ audio.src = tracks[currentIndex].url;
+ audio.load();
+
+ title.textContent = tracks[currentIndex].name;
+ status.textContent = "Ready to play.";
+ playButton.textContent = "Play";
+ playButton.disabled = false;
+ previousButton.disabled = tracks.length < 2;
+ nextButton.disabled = tracks.length < 2;
+
+ updateTimeline();
+ highlightTrack();
+
+ if (autoplay) startPlayback();
+}
+
+filesInput.addEventListener("change", () => {
+ const selectedFiles = Array.from(filesInput.files);
+ if (!selectedFiles.length) return;
+
+ playbackRequest++;
+ audio.pause();
+ audio.removeAttribute("src");
+ audio.load();
+
+ // Release the old file references before replacing the playlist.
+ tracks.forEach((track) => URL.revokeObjectURL(track.url));
+
+ tracks = selectedFiles.map((file) => ({
+ name: file.name,
+ url: URL.createObjectURL(file)
+ }));
+
+ playlist.replaceChildren();
+ empty.hidden = true;
+
+ tracks.forEach((track, index) => {
+ const item = document.createElement("li");
+ const button = document.createElement("button");
+
+ button.type = "button";
+ button.textContent = track.name;
+ button.addEventListener("click", () => selectTrack(index, true));
+
+ item.appendChild(button);
+ playlist.appendChild(item);
+ });
+
+ selectTrack(0);
+ filesInput.value = "";
+});
+
+playButton.addEventListener("click", () => {
+ if (audio.paused) {
+ startPlayback();
+ } else {
+ playbackRequest++;
+ audio.pause();
+ }
+});
+
+previousButton.addEventListener("click", () => {
+ selectTrack(currentIndex - 1, true);
+});
+
+nextButton.addEventListener("click", () => {
+ selectTrack(currentIndex + 1, true);
+});
+
+audio.addEventListener("play", () => {
+ playButton.textContent = "Pause";
+ status.textContent = "Playing.";
+});
+
+audio.addEventListener("pause", () => {
+ playButton.textContent = "Play";
+ if (currentIndex >= 0) status.textContent = "Paused.";
+});
+
+audio.addEventListener("ended", () => {
+ if (currentIndex < tracks.length - 1) {
+ selectTrack(currentIndex + 1, true);
+ } else {
+ status.textContent = "Playlist finished.";
+ }
+});
+
+audio.addEventListener("error", () => {
+ if (!audio.getAttribute("src")) return;
+
+ playButton.textContent = "Play";
+ status.textContent =
+ "This file could not be loaded. Choose another track or file.";
+});
+
+audio.addEventListener("loadedmetadata", updateTimeline);
+audio.addEventListener("durationchange", updateTimeline);
+audio.addEventListener("timeupdate", updateTimeline);
+
+seek.addEventListener("input", () => {
+ if (Number.isFinite(audio.duration) && audio.duration > 0) {
+ audio.currentTime = Number(seek.value);
+ updateTimeline();
+ }
+});
+
+volume.addEventListener("input", () => {
+ audio.volume = Number(volume.value);
+});
\ No newline at end of file
diff --git a/MusicPlayer/cobie1818/style.css b/MusicPlayer/cobie1818/style.css
new file mode 100644
index 000000000..1972946a4
--- /dev/null
+++ b/MusicPlayer/cobie1818/style.css
@@ -0,0 +1,199 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ padding: 32px 16px;
+ background: #101521;
+ color: #f3f5fa;
+ font-family: Arial, sans-serif;
+ line-height: 1.5;
+}
+
+.player {
+ width: 100%;
+ max-width: 580px;
+ margin: auto;
+ padding: 32px;
+ background: #1b2333;
+ border: 1px solid #39465d;
+ border-radius: 24px;
+ box-shadow: 0 20px 50px #0005;
+}
+
+header {
+ text-align: center;
+}
+
+h1 {
+ margin: 8px 0;
+ font-size: 2.4rem;
+}
+
+h2 {
+ font-size: 1.2rem;
+ overflow-wrap: anywhere;
+}
+
+p {
+ color: #c4ccdc;
+}
+
+.eyebrow {
+ color: #99e6d4;
+ font-size: 0.75rem;
+ letter-spacing: 2px;
+}
+
+.artwork {
+ display: grid;
+ place-items: center;
+ width: 160px;
+ height: 160px;
+ margin: 28px auto;
+ border-radius: 32px;
+ background: linear-gradient(135deg, #8de1cc, #9bafff);
+ color: #142137;
+ font-size: 5rem;
+}
+
+#now-playing,
+#status {
+ text-align: center;
+}
+
+label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: bold;
+}
+
+input[type="file"] {
+ width: 100%;
+ min-width: 0;
+ font: inherit;
+}
+
+input::file-selector-button {
+ padding: 10px;
+ margin-right: 8px;
+ border: 0;
+ border-radius: 8px;
+ cursor: pointer;
+}
+
+.hint {
+ font-size: 0.85rem;
+}
+
+.timeline,
+.volume-control {
+ margin-top: 24px;
+}
+
+input[type="range"] {
+ width: 100%;
+ min-height: 28px;
+ margin: 0;
+ accent-color: #99e6d4;
+}
+
+.times {
+ display: flex;
+ justify-content: space-between;
+ color: #c4ccdc;
+ font-size: 0.9rem;
+}
+
+.controls {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-top: 20px;
+}
+
+button {
+ min-height: 44px;
+ padding: 10px 14px;
+ border: 1px solid #667895;
+ border-radius: 10px;
+ background: #28364d;
+ color: #fff;
+ font: inherit;
+ cursor: pointer;
+}
+
+.controls button {
+ flex: 1;
+}
+
+button.primary {
+ background: #99e6d4;
+ color: #10241f;
+ border-color: #99e6d4;
+ font-weight: bold;
+}
+
+button:hover:not(:disabled) {
+ filter: brightness(1.15);
+}
+
+button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+:focus-visible {
+ outline: 3px solid #ffdb85;
+ outline-offset: 4px;
+}
+
+.playlist-section {
+ margin-top: 28px;
+ border-top: 1px solid #39465d;
+ padding-top: 16px;
+}
+
+#playlist {
+ padding-left: 24px;
+}
+
+#playlist li {
+ margin-bottom: 10px;
+}
+
+#playlist button {
+ width: 100%;
+ text-align: left;
+ overflow-wrap: anywhere;
+}
+
+#playlist button[aria-current="true"] {
+ border: 2px solid #99e6d4;
+ background: #304b4b;
+}
+
+@media (max-width: 400px) {
+ body {
+ padding: 12px;
+ }
+
+ .player {
+ padding: 20px 16px;
+ }
+
+ h1 {
+ font-size: 2rem;
+ }
+
+ .controls {
+ gap: 6px;
+ }
+
+ .controls button {
+ padding: 10px 8px;
+ font-size: 0.9rem;
+ }
+}
\ No newline at end of file