Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (() => {
- // Remove existing HUD if any
- const existing = document.getElementById('webcamASCIIHUD');
- if (existing) existing.remove();
- // Create HUD container
- const hud = document.createElement('div');
- hud.id = 'webcamASCIIHUD';
- Object.assign(hud.style, {
- position: 'fixed',
- top: '20px',
- left: '20px',
- width: '90%',
- maxWidth: '480px',
- height: 'auto',
- background: 'rgba(34,34,34,0.95)',
- borderRadius: '12px',
- padding: '15px',
- zIndex: 99999,
- fontFamily: 'Arial, sans-serif',
- fontSize: '12px',
- color: '#fff',
- boxShadow: '0 8px 25px rgba(0,0,0,0.4)',
- cursor: 'move',
- backdropFilter: 'blur(10px)',
- border: '2px solid #666',
- boxSizing: 'border-box',
- overflow: 'hidden',
- minWidth: '200px',
- minHeight: '150px'
- });
- // Close button
- const closeBtn = document.createElement('button');
- closeBtn.innerHTML = '×';
- Object.assign(closeBtn.style, {
- position: 'absolute',
- top: '8px',
- right: '8px',
- background: 'rgba(220,53,69,0.8)',
- color: '#fff',
- border: 'none',
- borderRadius: '50%',
- width: '20px',
- height: '20px',
- cursor: 'pointer',
- fontSize: '14px',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- });
- closeBtn.onclick = () => hud.remove();
- hud.appendChild(closeBtn);
- // Title
- const title = document.createElement('div');
- title.innerText = '🎥 Webcam ASCII Controller';
- Object.assign(title.style, { fontWeight: 'bold', fontSize: '14px', marginBottom: '12px' });
- hud.appendChild(title);
- // Controls container
- const controlsContainer = document.createElement('div');
- Object.assign(controlsContainer.style, { display: 'flex', flexDirection: 'column', gap: '10px', boxSizing: 'border-box' });
- hud.appendChild(controlsContainer);
- // Camera selection
- const camSelectDiv = document.createElement('div');
- camSelectDiv.innerHTML = '<strong>Camera:</strong> ';
- const camSelect = document.createElement('select');
- Object.assign(camSelect.style, { background: '#333', color: '#fff', border: '1px solid #555', padding: '3px', borderRadius: '4px', marginLeft: '5px', width: '120px' });
- camSelectDiv.appendChild(camSelect);
- controlsContainer.appendChild(camSelectDiv);
- // Populate cameras
- async function populateCameras() {
- try {
- const devices = await navigator.mediaDevices.enumerateDevices();
- const videoDevices = devices.filter(d => d.kind === 'videoinput');
- camSelect.innerHTML = '<option value="">Select Camera</option>';
- videoDevices.forEach((device, i) => {
- const option = document.createElement('option');
- option.value = device.deviceId;
- option.textContent = device.label || `Camera ${i + 1}`;
- camSelect.appendChild(option);
- });
- } catch (err) {
- console.log('Cannot enumerate devices:', err);
- }
- }
- // Resolution
- const resolutionDiv = document.createElement('div');
- resolutionDiv.innerHTML = '<strong>Resolution:</strong> ';
- const resolutionSelect = document.createElement('select');
- Object.assign(resolutionSelect.style, { background: '#333', color: '#fff', border: '1px solid #555', padding: '3px', borderRadius: '4px', marginLeft: '5px', width: '120px' });
- const resolutions = [
- { width: 160, height: 120, name: 'Low (160x120)' },
- { width: 320, height: 240, name: 'Medium (320x240)' },
- { width: 640, height: 480, name: 'High (640x480)' },
- { width: 1280, height: 720, name: 'HD (1280x720)' },
- { width: 1920, height: 1080, name: 'Full HD (1920x1080)' }
- ];
- resolutions.forEach(res => {
- const option = document.createElement('option');
- option.value = JSON.stringify(res);
- option.textContent = res.name;
- resolutionSelect.appendChild(option);
- });
- resolutionDiv.appendChild(resolutionSelect);
- controlsContainer.appendChild(resolutionDiv);
- // Quality
- const qualityDiv = document.createElement('div');
- qualityDiv.innerHTML = '<strong>Quality:</strong> ';
- const qualitySelect = document.createElement('select');
- Object.assign(qualitySelect.style, { background: '#333', color: '#fff', border: '1px solid #555', padding: '3px', borderRadius: '4px', marginLeft: '5px', width: '120px' });
- ['low','medium','high'].forEach(q => {
- const option = document.createElement('option');
- option.value = q;
- option.textContent = q.charAt(0).toUpperCase() + q.slice(1);
- qualitySelect.appendChild(option);
- });
- qualityDiv.appendChild(qualitySelect);
- controlsContainer.appendChild(qualityDiv);
- // Frame rate
- const frameRateDiv = document.createElement('div');
- frameRateDiv.innerHTML = '<strong>Frame Rate:</strong> <input type="range" id="frameRateSlider" min="1" max="30" value="10" style="width:100px"> <span id="frameRateValue">10</span> FPS';
- controlsContainer.appendChild(frameRateDiv);
- // Brightness, Contrast, Saturation
- const processingDiv = document.createElement('div');
- Object.assign(processingDiv.style, { display: 'flex', gap: '10px', flexWrap: 'wrap' });
- ['Brightness','Contrast','Saturation'].forEach(label=>{
- const div = document.createElement('div');
- const id = label.toLowerCase()+'Slider';
- div.innerHTML = `<div style="font-size:10px; opacity:0.8;">${label}</div><input type="range" id="${id}" min="-100" max="100" value="0" style="width:100px"> <span id="${id}Value">0</span>`;
- processingDiv.appendChild(div);
- });
- controlsContainer.appendChild(processingDiv);
- // Action buttons
- const actionButtonsDiv = document.createElement('div');
- Object.assign(actionButtonsDiv.style, { display: 'flex', gap: '8px', flexWrap:'wrap', marginTop:'8px' });
- const startBtn = document.createElement('button');
- startBtn.innerText = '▶ Start Webcam';
- Object.assign(startBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#28a745,#20c997)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- const stopBtn = document.createElement('button');
- stopBtn.innerText = '⏹ Stop';
- Object.assign(stopBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#dc3545,#e83e8c)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- const snapshotBtn = document.createElement('button');
- snapshotBtn.innerText = '📸 Snapshot';
- Object.assign(snapshotBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#fd7e14,#ffc107)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- const invertBtn = document.createElement('button');
- invertBtn.innerText = '🎨 Invert: Off';
- Object.assign(invertBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#6f42c1,#6610f2)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- const mirrorBtn = document.createElement('button');
- mirrorBtn.innerText = 'Flip: None';
- Object.assign(mirrorBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#17a2b8,#20c997)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- const brailleBtn = document.createElement('button');
- brailleBtn.innerText = 'Braille: Off';
- Object.assign(brailleBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#ff6f61,#ff9a76)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- // === New Select All button ===
- const selectAllBtn = document.createElement('button');
- selectAllBtn.innerText = '🔲 Select All';
- Object.assign(selectAllBtn.style, { flex: '1', padding:'6px', background: 'linear-gradient(45deg,#0d6efd,#6610f2)', color:'#fff', border:'none', borderRadius:'6px', cursor:'pointer', fontSize:'11px' });
- actionButtonsDiv.append(startBtn, stopBtn, snapshotBtn, invertBtn, mirrorBtn, brailleBtn, selectAllBtn);
- controlsContainer.appendChild(actionButtonsDiv);
- // Status display
- const statusDiv = document.createElement('div');
- statusDiv.id = 'webcamStatus';
- statusDiv.innerText = 'Ready - Select camera and resolution';
- Object.assign(statusDiv.style, { marginTop:'10px', padding:'6px', background:'rgba(50,50,80,0.3)', borderRadius:'4px', fontSize:'10px', color:'#4CAF50' });
- controlsContainer.appendChild(statusDiv);
- // Preview
- const pre = document.createElement('pre');
- Object.assign(pre.style, {
- background:'#111',
- color:'#ccc',
- fontSize:'8px',
- lineHeight:'7px',
- padding:'8px',
- borderRadius:'6px',
- marginTop:'12px',
- overflow:'auto',
- maxHeight:'300px',
- width:'100%',
- border:'1px solid #333',
- whiteSpace:'pre-wrap',
- wordBreak:'break-word'
- });
- hud.appendChild(pre);
- document.body.appendChild(hud);
- // === Select All functionality ===
- selectAllBtn.addEventListener('click', () => {
- const range = document.createRange();
- range.selectNodeContents(pre);
- const selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
- statusDiv.innerText = 'ASCII selected — press Ctrl+C to copy';
- statusDiv.style.color = '#4CAF50';
- });
- // Resize handle
- const resizeHandle = document.createElement('div');
- Object.assign(resizeHandle.style, {
- width: '12px',
- height: '12px',
- position: 'absolute',
- bottom: '4px',
- right: '4px',
- cursor: 'nwse-resize',
- background: 'rgba(255,255,255,0.3)',
- borderRadius: '2px',
- });
- hud.appendChild(resizeHandle);
- // State variables
- let currentStream = null;
- let currentVideo = null;
- let canvas = document.createElement('canvas');
- let selectedResolutionGlobal = { width: 320, height: 240 };
- let animationRunning = false;
- let invertASCII = false;
- let mirrorMode = 'none'; // none | horizontal | vertical
- let brailleMode = false;
- // Invert toggle
- invertBtn.addEventListener('click', () => { invertASCII = !invertASCII; invertBtn.innerText = `🎨 Invert: ${invertASCII ? 'On':'Off'}`; });
- // Mirror toggle
- mirrorBtn.addEventListener('click', () => {
- if(mirrorMode==='none') mirrorMode='horizontal';
- else if(mirrorMode==='horizontal') mirrorMode='vertical';
- else mirrorMode='none';
- mirrorBtn.innerText = `Flip: ${mirrorMode.charAt(0).toUpperCase()+mirrorMode.slice(1)}`;
- });
- // Braille toggle
- brailleBtn.addEventListener('click', () => { brailleMode=!brailleMode; brailleBtn.innerText=`Braille: ${brailleMode?'On':'Off'}`; });
- // Slider displays
- const updateSliderDisplay = (sliderId, valueId) => {
- const slider = document.getElementById(sliderId);
- const valueDisplay = document.getElementById(valueId);
- if(slider && valueDisplay){
- slider.addEventListener('input', ()=>{ valueDisplay.textContent=slider.value; });
- }
- };
- ['frameRateSlider','brightnessSlider','contrastSlider','saturationSlider'].forEach(id=>{
- const valueId = id==='frameRateSlider'?'frameRateValue':id+'Value';
- updateSliderDisplay(id,valueId);
- });
- // Start webcam
- async function startWebcam(){
- try{
- statusDiv.innerText='Starting webcam...'; statusDiv.style.color='#FF9800';
- selectedResolutionGlobal = JSON.parse(resolutionSelect.value);
- const constraints = {
- video:{
- deviceId: camSelect.value ? { exact: camSelect.value } : true,
- width: { ideal: selectedResolutionGlobal.width },
- height: { ideal: selectedResolutionGlobal.height }
- }
- };
- currentStream = await navigator.mediaDevices.getUserMedia(constraints);
- currentVideo = document.createElement('video');
- currentVideo.srcObject = currentStream;
- currentVideo.autoplay=true;
- currentVideo.playsInline=true;
- currentVideo.muted=true;
- currentVideo.style.display='none';
- document.body.appendChild(currentVideo);
- currentVideo.addEventListener('loadeddata', async () => {
- await currentVideo.play();
- statusDiv.innerText = `Webcam active - ${selectedResolutionGlobal.width}x${selectedResolutionGlobal.height}`;
- statusDiv.style.color = '#4CAF50';
- if(!animationRunning){
- animationRunning = true;
- const loop = () => {
- if(currentVideo) {
- processFrame(currentVideo);
- const fps = parseInt(document.getElementById('frameRateSlider').value);
- setTimeout(()=>requestAnimationFrame(loop),1000/fps);
- } else { animationRunning=false; }
- };
- loop();
- }
- });
- startBtn.disabled=true; stopBtn.disabled=false; snapshotBtn.disabled=false;
- }catch(e){
- console.error('Webcam error:',e);
- statusDiv.innerText=`Error: ${e.message}`; statusDiv.style.color='#f44336';
- }
- }
- // Stop webcam
- function stopWebcam(){
- if(currentStream) currentStream.getTracks().forEach(t=>t.stop());
- if(currentVideo){ currentVideo.pause(); currentVideo.srcObject=null; currentVideo.remove(); currentVideo=null; }
- startBtn.disabled=false; stopBtn.disabled=true; snapshotBtn.disabled=true;
- statusDiv.innerText='Webcam stopped'; statusDiv.style.color='#aaa';
- }
- // Snapshot
- function takeSnapshot(){ if(currentVideo) processFrame(currentVideo); }
- // Frame processing
- function processFrame(videoEl){
- const hudWidth = hud.clientWidth - 16;
- const hudHeight = hud.clientHeight - pre.offsetTop - 16;
- const charWidth = 6, charHeight = 7;
- let asciiCols = Math.floor(hudWidth / charWidth);
- let asciiRows = Math.floor(hudHeight / charHeight);
- let width = asciiCols;
- let height = asciiRows;
- if(brailleMode){ width*=2; height*=4; }
- canvas.width=width; canvas.height=height;
- const ctx=canvas.getContext('2d');
- try{
- ctx.save();
- if(mirrorMode==='horizontal'){ ctx.translate(canvas.width,0); ctx.scale(-1,1); }
- else if(mirrorMode==='vertical'){ ctx.translate(0,canvas.height); ctx.scale(1,-1); }
- ctx.drawImage(videoEl,0,0,width,height);
- ctx.restore();
- const data=ctx.getImageData(0,0,width,height).data;
- let output='';
- const brightness=parseInt(document.getElementById('brightnessSlider').value);
- const contrast=parseInt(document.getElementById('contrastSlider').value);
- const saturation=parseInt(document.getElementById('saturationSlider').value);
- if(brailleMode){
- for(let y=0;y<height;y+=4){
- for(let x=0;x<width;x+=2){
- let brailleChar=0x2800;
- const dotMap=[[0,3],[1,4],[2,5],[6,7]];
- for(let py=0;py<4;py++){
- for(let px=0;px<2;px++){
- const xi=x+px, yi=y+py;
- if(xi>=width||yi>=height) continue;
- const i=(yi*width+xi)*4;
- let r=data[i],g=data[i+1],b=data[i+2];
- if(brightness!==0){ const f=1+brightness/100; r=Math.min(255,Math.max(0,r*f)); g=Math.min(255,Math.max(0,g*f)); b=Math.min(255,Math.max(0,b*f)); }
- if(contrast!==0){ const f=(259*(contrast+255))/(255*(259-contrast)); r=Math.min(255,Math.max(0,f*(r-128)+128)); g=Math.min(255,Math.max(0,f*(g-128)+128)); b=Math.min(255,Math.max(0,f*(b-128)+128)); }
- if(saturation!==0){ const gray=0.299*r+0.587*g+0.114*b; const f=1+saturation/100; r=Math.min(255,Math.max(0,gray+(r-gray)*f)); g=Math.min(255,Math.max(0,gray+(g-gray)*f)); b=Math.min(255,Math.max(0,gray+(b-gray)*f)); }
- const lum=(0.299*r+0.587*g+0.114*b)/255;
- if(lum>0.5){ brailleChar|=1<<dotMap[py][px]; }
- }
- }
- output+=String.fromCharCode(brailleChar);
- }
- output+='\n';
- }
- } else {
- const chars = ['@','W','M','#','%','&','8','B','D','H','Q','O','0','9','5','3','2','1','|','!',';','^','~',':',',','.',' '];
- for(let y=0;y<height;y++){
- for(let x=0;x<width;x++){
- const i=(y*width+x)*4;
- let r=data[i],g=data[i+1],b=data[i+2];
- if(brightness!==0){ const f=1+brightness/100; r=Math.min(255,Math.max(0,r*f)); g=Math.min(255,Math.max(0,g*f)); b=Math.min(255,Math.max(0,b*f)); }
- if(contrast!==0){ const f=(259*(contrast+255))/(255*(259-contrast)); r=Math.min(255,Math.max(0,f*(r-128)+128)); g=Math.min(255,Math.max(0,f*(g-128)+128)); b=Math.min(255,Math.max(0,f*(b-128)+128)); }
- if(saturation!==0){ const gray=0.299*r+0.587*g+0.114*b; const f=1+saturation/100; r=Math.min(255,Math.max(0,gray+(r-gray)*f)); g=Math.min(255,Math.max(0,gray+(g-gray)*f)); b=Math.min(255,Math.max(0,gray+(b-gray)*f)); }
- const lum=(0.299*r+0.587*g+0.114*b)/255;
- const charIndex=Math.floor(lum*(chars.length-1));
- output+=invertASCII ? chars[chars.length-1-charIndex] : chars[charIndex];
- }
- output+='\n';
- }
- }
- pre.innerText=output;
- }catch(e){ console.log('Frame processing error:',e); }
- }
- // Event listeners
- startBtn.addEventListener('click', startWebcam);
- stopBtn.addEventListener('click', stopWebcam);
- snapshotBtn.addEventListener('click', takeSnapshot);
- // Populate cameras
- populateCameras();
- // Dragging
- let isDragging=false, offsetX=0, offsetY=0;
- hud.addEventListener('mousedown',e=>{
- if(['BUTTON','INPUT','SELECT'].includes(e.target.tagName)) return;
- isDragging=true;
- offsetX=e.clientX-hud.offsetLeft; offsetY=e.clientY-hud.offsetTop;
- hud.style.cursor='grabbing';
- });
- document.addEventListener('mousemove',e=>{ if(!isDragging) return; hud.style.left=(e.clientX-offsetX)+'px'; hud.style.top=(e.clientY-offsetY)+'px'; });
- document.addEventListener('mouseup',()=>{ isDragging=false; hud.style.cursor='move'; });
- // Resizing
- let isResizing=false, resizeStartX=0, resizeStartY=0, startWidth=0, startHeight=0;
- resizeHandle.addEventListener('mousedown',e=>{
- e.stopPropagation(); isResizing=true; resizeStartX=e.clientX; resizeStartY=e.clientY;
- startWidth=hud.offsetWidth; startHeight=hud.offsetHeight;
- });
- document.addEventListener('mousemove',e=>{
- if(!isResizing) return;
- hud.style.width=startWidth+(e.clientX-resizeStartX)+'px';
- hud.style.height=startHeight+(e.clientY-resizeStartY)+'px';
- });
- document.addEventListener('mouseup',()=>{ isResizing=false; });
- console.log('🎥 Webcam ASCII Controller loaded with Braille mode and Select All button!');
- })();
Advertisement
Add Comment
Please, Sign In to add comment