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
12 changes: 12 additions & 0 deletions Javascript/Real-Time Filter Camera/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 🎥 Live Webcam Filters

A real-time webcam filter application built with HTML5 Canvas and vanilla JavaScript. Apply fun visual effects to your webcam feed directly in the browser!

## ✨ Features
- 🎨 Normal (no filter)
- ⚫ Grayscale (black & white)
- 🟤 Sepia (vintage photo)
- 🔄 Invert (negative colors)
- 🌫️ Blur (soft focus)
- ☀️ Brightness (enhanced lighting)
- 🎭 Contrast (dramatic tones)
38 changes: 38 additions & 0 deletions Javascript/Real-Time Filter Camera/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webcam Filters</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>🎥 Live Webcam Filters</h1>

<div class="video-container">
<video id="video" autoplay playsinline></video>
<canvas id="canvas"></canvas>
</div>

<div class="controls">
<button id="startBtn">Start Webcam</button>

<div class="button-group">
<button class="filter-btn active" data-filter="none">Normal</button>
<button class="filter-btn" data-filter="grayscale">Grayscale</button>
<button class="filter-btn" data-filter="sepia">Sepia</button>
<button class="filter-btn" data-filter="invert">Invert</button>
<button class="filter-btn" data-filter="blur">Blur</button>
<button class="filter-btn" data-filter="brightness">Bright</button>
<button class="filter-btn" data-filter="contrast">Contrast</button>
</div>

<div class="status" id="status">Click "Start Webcam" to begin</div>
<div class="filter-label" id="filterLabel">Current Filter: Normal</div>
</div>
</div>

<script src="script.js"></script>
</body>
</html>
137 changes: 137 additions & 0 deletions Javascript/Real-Time Filter Camera/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const startBtn = document.getElementById('startBtn');
const filterBtns = document.querySelectorAll('.filter-btn');
const status = document.getElementById('status');
const filterLabel = document.getElementById('filterLabel');

let currentFilter = 'none';
let stream = null;
let animationId = null;
let isWebcamActive = false;

startBtn.addEventListener('click', async () => {
if (isWebcamActive) {
stopWebcam();
return;
}

try {
stream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480 }
});
video.srcObject = stream;

video.addEventListener('loadedmetadata', () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
startBtn.textContent = 'Stop Webcam';
startBtn.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)';
status.textContent = 'Webcam active! Select a filter below';
isWebcamActive = true;
applyFilter();
});
} catch (err) {
status.textContent = 'Error: Could not access webcam';
console.error('Webcam error:', err);
}
});

function stopWebcam() {
if (stream) {
stream.getTracks().forEach(track => track.stop());
stream = null;
}
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
video.srcObject = null;
ctx.clearRect(0, 0, canvas.width, canvas.height);
startBtn.textContent = 'Start Webcam';
startBtn.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
status.textContent = 'Webcam stopped. Click to restart';
isWebcamActive = false;
}

filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
const filterName = btn.textContent;
filterLabel.textContent = `Current Filter: ${filterName}`;
});
});

function applyFilter() {
if (!video.paused && !video.ended) {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

if (currentFilter !== 'none') {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;

switch(currentFilter) {
case 'grayscale':
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg;
data[i + 1] = avg;
data[i + 2] = avg;
}
break;

case 'sepia':
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
}
break;

case 'invert':
for (let i = 0; i < data.length; i += 4) {
data[i] = 255 - data[i];
data[i + 1] = 255 - data[i + 1];
data[i + 2] = 255 - data[i + 2];
}
break;

case 'blur':
ctx.filter = 'blur(4px)';
ctx.drawImage(canvas, 0, 0);
ctx.filter = 'none';
break;

case 'brightness':
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, data[i] * 1.5);
data[i + 1] = Math.min(255, data[i + 1] * 1.5);
data[i + 2] = Math.min(255, data[i + 2] * 1.5);
}
break;

case 'contrast':
const factor = 1.5;
const intercept = 128 * (1 - factor);
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.min(255, Math.max(0, data[i] * factor + intercept));
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] * factor + intercept));
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] * factor + intercept));
}
break;
}

if (currentFilter !== 'blur') {
ctx.putImageData(imageData, 0, 0);
}
}

animationId = requestAnimationFrame(applyFilter);
}
}
125 changes: 125 additions & 0 deletions Javascript/Real-Time Filter Camera/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(180deg, #070507 0%, #1a50b3 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}

.container {
background: linear-gradient(180deg, #f9a425e5 0%, #c71b1b 100%);
border-radius: 20px;
padding: 30px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 900px;
width: 100%;
}

h1 {
text-align: center;
color: #333;
margin-bottom: 25px;
font-size: 2em;
}

.video-container {
position: relative;
width: 100%;
max-width: 640px;
margin: 0 auto 25px;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

#video {
display: none;
}

#canvas {
width: 100%;
height: auto;
display: block;
background: #000;
}

.controls {
display: flex;
flex-direction: column;
gap: 15px;
}

.button-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}

button {
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
border: none;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
color: white;
text-transform: uppercase;
letter-spacing: 0.5px;
}

#startBtn {
background: linear-gradient(135deg, #4877f9 0%, #50158f 100%);
}

#startBtn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}

.filter-btn {
background: linear-gradient(135deg, #f093fb 0%, #bbe1b8 100%);
min-width: 120px;
}

.filter-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(245, 87, 108, 0.4);
}

.filter-btn.active {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
box-shadow: 0 0 20px rgba(79, 172, 254, 0.5);
}

button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none !important;
}

.status {
text-align: center;
padding: 15px;
border-radius: 10px;
background: linear-gradient(135deg, #f1f1f0 0%, #faf7f6 100%);
color: #333;
font-weight: 500;
}

.filter-label {
text-align: center;
font-size: 14px;
color: #f8f5f5;
margin-top: 10px;
}
Loading