# Create audio call website with html and websocket server ### Steps 1. Create a websocket server using [Replit](https://replit.com/~) 2. Create a html website using [Glitch](https://glitch.com/) (actually can use Replit also) 3. Audio Call website done! ### How to use Replit to host a websocket server 1. Go to [Replit](https://replit.com/~) and sign up 2. Choose "Create Node.js" 3. Put your code in index.js 4. Press "Run" button ### How to use Glicth to host a html website 1. Go to [Glitch](https://glitch.com/) and sign up 2. Choose "New Project" -> "glitch-hello-website" 3. Put your code in index.html 4. Copy your website url 5. Explore your website on two device 6. Enjoy audio call ``` javascript // websocket server code const WebSocket = require('ws'); const server = new WebSocket.Server({ port: 8080 }); server.on('connection', socket => { socket.on('message', message => { if (Buffer.isBuffer(message)) { console.log('Received binary data:', message); } else { console.log('Received text data:', message); } // 转发或处理消息 server.clients.forEach(client => { if (client !== socket && client.readyState === WebSocket.OPEN) { client.send(message); } }); }); }); console.log('WebSocket server is running on ws://localhost:8080'); ``` remember to replace replit-repo-url with your own replit repo's url ```html <!-- website code--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>WebSocket Voice Chat</title> <!-- 引入 Bootstrap CSS --> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" /> <!-- 引入 Font Awesome 用於圖標 --> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet" /> <style> body { background-color: #f8f9fa; font-family: Arial, sans-serif; height: 100vh; margin: 0; display: flex; justify-content: center; align-items: center; } .container { max-width: 400px; } h1 { font-size: 24px; margin-bottom: 20px; } .btn { margin: 5px; width: 70px; height: 70px; border-radius: 50%; font-size: 24px; } .btn-wrapper { display: flex; justify-content: space-between; } #status { margin-bottom: 20px; font-weight: bold; text-align: center; } </style> </head> <body> <div class="container text-center"> <div class="card"> <div class="card-body"> <h1 class="card-title">Voice Chat</h1> <div id="status" class="alert alert-secondary">未接通</div> <div class="btn-wrapper"> <button id="startCall" class="btn btn-primary"> <i class="fas fa-phone"></i> </button> <button id="endCall" class="btn btn-danger" disabled> <i class="fas fa-phone-slash"></i> </button> <button id="toggleMic" class="btn btn-secondary" disabled> <i class="fas fa-microphone"></i> </button> </div> </div> </div> </div> <script> const statusDiv = document.getElementById("status"); const startButton = document.getElementById("startCall"); const endButton = document.getElementById("endCall"); const toggleMicButton = document.getElementById("toggleMic"); let socket; let audioContext; let processor; let micEnabled = true; let audioStream; startButton.addEventListener("click", () => { statusDiv.textContent = "接通中..."; socket = new WebSocket( "wss://replit-repo-url:8080" ); socket.binaryType = 'arraybuffer'; socket.onopen = () => { console.log("WebSocket connected"); statusDiv.textContent = "接通成功"; statusDiv.className = "alert alert-success"; startButton.disabled = true; endButton.disabled = false; toggleMicButton.disabled = false; audioContext = new (window.AudioContext || window.webkitAudioContext)(); navigator.mediaDevices .getUserMedia({ audio: true }) .then((stream) => { audioStream = stream; const source = audioContext.createMediaStreamSource(stream); processor = audioContext.createScriptProcessor(4096, 1, 1); source.connect(processor); processor.connect(audioContext.destination); processor.onaudioprocess = (e) => { if (socket.readyState === WebSocket.OPEN && micEnabled) { const audioData = e.inputBuffer.getChannelData(0); socket.send(audioData.buffer); console.log(audioData.buffer); } }; console.log("Audio stream processing started"); }) .catch((error) => { console.error("Error accessing audio stream:", error); statusDiv.textContent = "无法获取音频流: " + error.message; statusDiv.className = "alert alert-danger"; }); }; socket.onmessage = (event) => { statusDiv.textContent = "对方说话中..."; statusDiv.className = "alert alert-info"; console.log('Received data:', event.data); const float32Array = new Float32Array(event.data); const audioBuffer = audioContext.createBuffer(1, float32Array.length, audioContext.sampleRate); audioBuffer.getChannelData(0).set(float32Array); const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(); console.log("Audio playback started"); }; socket.onclose = () => { statusDiv.textContent = "连接已关闭"; statusDiv.className = "alert alert-warning"; console.log("WebSocket connection closed"); startButton.disabled = false; endButton.disabled = true; toggleMicButton.disabled = true; stopAudioStream(); }; socket.onerror = (error) => { statusDiv.textContent = "连接错误"; statusDiv.className = "alert alert-danger"; console.error("WebSocket error:", error); stopAudioStream(); }; }); endButton.addEventListener("click", () => { if (socket) { socket.close(); statusDiv.textContent = "通話已結束"; statusDiv.className = "alert alert-secondary"; startButton.disabled = false; endButton.disabled = true; toggleMicButton.disabled = true; stopAudioStream(); } }); toggleMicButton.addEventListener("click", () => { micEnabled = !micEnabled; toggleMicButton.innerHTML = micEnabled ? '<i class="fas fa-microphone"></i>' : '<i class="fas fa-microphone-slash"></i>'; console.log(`Mic is now ${micEnabled ? "enabled" : "disabled"}`); }); function stopAudioStream() { if (processor) { processor.onaudioprocess = null; processor.disconnect(); processor = null; } if (audioStream) { audioStream.getTracks().forEach(track => track.stop()); audioStream = null; } if (audioContext) { audioContext.close(); audioContext = null; } } </script> <!-- 引入 Bootstrap JS 和 Popper.js (Bootstrap 依賴項) --> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.0.7/dist/umd/popper.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> </body> </html> ```