Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions submissions/Noraku/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# **Noraku** 🎵

![Noraku Logo](icons/noraku_128.png)

A browser extension that enhances your browsing experience by adding immersive sound effects to various actions!

> **Note:** This extension is still in development, and more features will be added soon! 🚀

---

## **Features 😎**

✅ **Tab Open & Close Sounds** – Experience satisfying sound feedback when opening and closing tabs.
✅ **Action-Based Sound Effects** – Hear unique sounds for actions like bookmarking, downloading, muting tabs, and more.
✅ **Keypress Sounds** – Custom sound effects for different key types (A-Z, Spacebar, Function Keys, etc.).
✅ **Search Interaction Sounds** – Audio feedback when interacting with search bars.
✅ **Error & Block Action Sounds** – Get notified with sounds when an action fails or is blocked.
✅ **Theme Selection (Coming Soon)** – Choose from different anime/game themes with unique sound effects.
✅ **Custom Sound Support (Coming Soon)** – Upload your own sound effects for a personalized experience.
✅ **Website Integration (Planned)** – A full website where users can configure settings.

---

## **Privacy Policy 🔒**

Noraku does **not** collect, track, or share any user data. All settings are stored **locally** on your device. Your privacy is our priority.

---

## **Installation 🛠️**

Installing **Noraku** is simple!

1️⃣ **Download the Repository**
- Click the **"Code"** button on GitHub and select **"Download ZIP"**.
- Extract the ZIP file on your computer.

**OR**

Run this command in your terminal (**requires Git**):
```bash
git clone https://github.com/Diptanu761/noraku.git
```

2️⃣ **Load the Extension in Chrome**
- Open Google Chrome and go to chrome://extensions.
- Enable "Developer Mode" (toggle switch in the top right).
- Click "Load Unpacked" and select the Noraku folder.

3️⃣ **Done! 🎉 Enjoy the sound-enhanced browsing experience.**

---

## **To-Do list ✏️**

- [ ] Add a website where users can configure settings 🌐

---

## **Special Thanks 💖**

🎨 Logo Designed by: @ShibamRoy9826
Thank you for creating the Noraku logo and being a great help in this project! 🙌

---

## **Contributing 🙏**

We welcome contributions! 💙
- Found a bug? Report it by opening an issue.
- Have an idea? Suggest a feature request.
- Want to contribute? Fork the repository and submit a pull request.

> ✨ Please consider starring this repository if you like the project!
95 changes: 95 additions & 0 deletions submissions/Noraku/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// ✅ Ensure Offscreen Document Exists
async function ensureOffscreen() {
const hasDocument = await chrome.offscreen.hasDocument();

if (!hasDocument) {
try {
await chrome.offscreen.createDocument({
url: "offscreen.html",
reasons: ["AUDIO_PLAYBACK"],
justification: "Play sound effects in the background."
});
} catch (err) {
}
}
}

// ✅ Function to Play Sound with Volume Control
async function playSound(action) {
await ensureOffscreen();

chrome.storage.local.get(["volumes"], (data) => {
const volumes = data.volumes || {};
const volume = volumes[action] !== undefined ? volumes[action] / 100 : 0.5;

chrome.runtime.sendMessage({
action: "playSoundOffscreen",
sound: action,
volume: volume
});
});
}

// ✅ Handle Tab Open/Close Events
chrome.tabs.onCreated.addListener(() => playSound("tab_open"));
chrome.tabs.onRemoved.addListener(() => playSound("tab_close"));

// ✅ Handle Tab Dragging Events
chrome.tabs.onMoved.addListener(() => playSound("tab_dragging"));

// ✅ Handle Download Start/Complete Events
chrome.downloads.onCreated.addListener(() => playSound("download_start"));
chrome.downloads.onChanged.addListener((delta) => {
if (delta.state?.current === "complete") playSound("download_complete");
if (delta.state?.current === "interrupted") playSound("download_failed");
});

// ✅ Handle Bookmark Events
chrome.bookmarks.onCreated.addListener(() => playSound("bookmark_added"));

// ✅ Handle Tab Mute/Unmute Events
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
if (changeInfo.mutedInfo) {
const soundName = changeInfo.mutedInfo.muted ? "tab_muted" : "tab_unmuted";
playSound(soundName);
}
});

// ✅ Inject `content.js` into all active tabs on install & startup
chrome.runtime.onInstalled.addListener(() => injectContentScripts());
chrome.runtime.onStartup.addListener(() => injectContentScripts());

// ✅ Function to Inject `content.js`
function injectContentScripts() {
chrome.tabs.query({ url: ["http://*/*", "https://*/*"] }, (tabs) => {
for (let tab of tabs) {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content.js"]
})
}
});
}

// ✅ Inject `content.js` when YouTube pages load
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete" && tab.url.includes("youtube.com")) {
chrome.scripting.executeScript({
target: { tabId: tabId },
files: ["content.js"]
})
}
});

// ✅ Listen for Messages from Popup & Forward to Offscreen
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
if (message.action === "updateVolume") {
chrome.storage.local.set({ volumes: message.volumes }, () => {
console.log("🔊 Updated volumes:", message.volumes);
});
}

if (message.action === "playSound") {
await playSound(message.sound);
}
});
185 changes: 185 additions & 0 deletions submissions/Noraku/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
if (!chrome.runtime?.id) {
console.warn("❌ Extension context invalidated. Content script won't run.");
} else {

let audio = document.createElement("audio");
audio.style.display = "none";

const bd = document.querySelector("body");
bd.appendChild(audio);

let scrollCooldown = false;
let lastScrollTime = 0;
const SCROLL_DELAY = 300;

function playSound(soundName) {
let now = Date.now();
if (scrollCooldown || now - lastScrollTime < SCROLL_DELAY) return;

lastScrollTime = now;

if (chrome.runtime?.id) {
chrome.storage.local.get(["volumes"], (data) => {
const volumes = data.volumes || {};
const volume = volumes[soundName] !== undefined ? volumes[soundName] / 100 : 0.5; // Default 50%

if (chrome.runtime?.id) { // Additional check before accessing runtime API
audio.src = chrome.runtime.getURL(`sounds/${soundName}.mp3`);
audio.volume = volume;

audio.play().catch(() => {
// Catch and ignore any play() errors (e.g., user didn't interact)
});
}
});
}

scrollCooldown = true;
setTimeout(() => (scrollCooldown = false), SCROLL_DELAY);
}

window.addEventListener("wheel", () => playSound("scroll"), { passive: true });
window.addEventListener("touchmove", () => playSound("scroll"), { passive: true });

window.addEventListener("keydown", (event) => {
if (["ArrowUp", "ArrowDown", "PageUp", "PageDown"].includes(event.key)) {
playSound("scroll");
}
});

document.addEventListener("scroll", () => playSound("scroll"), { passive: true });

document.addEventListener("submit", () => {
chrome.runtime.sendMessage({ action: "playSound", sound: "form_submit" })
});

document.addEventListener("click", (event) => {
const element = event.target;

if (
element.tagName === "INPUT" && element.type === "text" ||
element.tagName === "INPUT" && element.type === "search" ||
element.tagName === "TEXTAREA" ||
element.isContentEditable
) {
playSound("search_focus");
}
});

document.addEventListener("keydown", (event) => {
let soundName = null;
let key = event.key;

if (key.match(/^[a-z]$/i)) {
soundName = "A_Z"; // A-Z and a-z keys
} else if (key.match(/^[0-9]$/)) {
soundName = "num-keys"; // Number keys above A-Z
} else if (["Control", "Shift", "Tab", "CapsLock", "Alt"].includes(key)) {
soundName = "special-keys"; // Special keys
} else if (key === "Enter") {
soundName = "enter"; // Enter key sound
} else if (key === "Backspace") {
soundName = "backspace"; // Backspace key
} else if (key === "Escape") {
soundName = "escape"; // Escape key
}
else if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(key)) {
playWithCooldown("arrow-keys", 150);
return;
} else if (key === " ") {
playWithCooldown("spacebar", 200);
return;
} else if (key.match(/^F[1-9]$|^F1[0-2]$/)) {
playWithCooldown("function-keys", 300);
return;
}

if (soundName) {
playSound(soundName);
}
});

// Function to handle cooldown-based key sounds
let keyCooldown = false;
function playWithCooldown(sound, delay) {
if (keyCooldown) return;

playSound(sound);
keyCooldown = true;

setTimeout(() => {
keyCooldown = false;
}, delay);
}

function detectVideos(root = document) {
root.querySelectorAll("video").forEach(video => {
if (!video.dataset.hasListener) {
video.dataset.hasListener = "true";

video.addEventListener("play", () => playSound("video_play"));
video.addEventListener("pause", () => playSound("video_pause"));
}
});
}

const observer = new MutationObserver(() => {
detectVideos();
});

observer.observe(document.body, { childList: true, subtree: true });

detectVideos();

if (window.location.hostname.includes("youtube.com")) {
setInterval(detectVideos, 1000);
}

document.addEventListener("click", () => playSound("click"));

document.addEventListener("mouseover", (event) => {
const element = event.target;

if (
element.tagName === "A" ||
element.tagName === "BUTTON" ||
element.hasAttribute("role") && element.getAttribute("role") === "button" ||
element.hasAttribute("data-clickable")
) {
playSound("hover");
}
});

document.addEventListener("contextmenu", (event) => {
// Do nothing special, allow the default right-click behavior
});

document.addEventListener("keydown", (event) => {
const blockedShortcuts = ["s", "u", "i", "j"];
if (event.ctrlKey && blockedShortcuts.includes(event.key.toLowerCase())) {
event.preventDefault();
playSound("error");
}
});

// ✅ Form Validation Errors
document.addEventListener("invalid", (event) => {
playSound("error");
}, true);

// ✅ Clicking Disabled Buttons
document.addEventListener("click", (event) => {
if (event.target.tagName === "BUTTON" && event.target.disabled) {
playSound("error");
}
});

// ✅ Download Failures
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "downloadFailed") {
playSound("error");
}
});
}


Binary file added submissions/Noraku/icons/noraku_128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/Noraku/icons/noraku_16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/Noraku/icons/noraku_48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/Noraku/icons/noraku_extra.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading