# 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>
```