JavaScript WebRTC. WebRTC example JavaScript 🌐Remote Controller
Tags: webdev, javascript, webrtc, tutorial
Published: 2024-03-19 07:15:00

As we have seen on the first post of this serie we have setup a **Pion WebRTC** "client". Now we are going to explore the "server" example built for the **browser** with JS(TS) and a little of WASM.

💭 Remember that this is modified and simplified example of "**Remote Controller**" my software product that tries to be an **alternative to Steam Remote Play**. If you are interested to learn more about how it works I will continue this serie, but for the moment you will have the codebase to [check it out](https://github.com/PiterWeb/RemoteController)

Lets start with the example:

First we will define the default STUN servers

```ts
const defaultStunServers = [
	'stun:stun.l.google.com:19302',
	'stun:stun.l.google.com:19305'
];
```

The next step will be the peerconnection creation and handle

```ts

const peerConnection = new RTCPeerConnection({
		iceServers: [
			{
				urls: defaultStunServers
			}
		]
});

/* fn will be an optional callback function to execute 
 when the close succeded */
function ClosePeerConnection(fn?: () => void) {
	if (fn) fn();
	peerConnection.close();
}

async function CreateServerWebRTC() {

	peerConnection.onconnectionstatechange = handleConnectionState;

    const numbersChannel = peerConnection.createDataChannel("numbers");

    const otherChannel = peerConnection.createDataChannel("other");

    numbersChannel.onmessage = (e) => {
        console.log("Number: ", e.data);
	}

	othersChannel.onopen = () => {
		othersChannel.send(JSON.stringify({'message': "Hello World from WebRTC"}))
	};

	let copiedCode: string = '';


		const offer = await peerConnection.createOffer();

		await peerConnection.setLocalDescription(offer);

		const candidates: RTCIceCandidateInit[] = [];

		peerConnection.onicecandidate = (ev) => {
			if (ev.candidate === null) {

				copiedCode =
					signalEncode(peerConnection?.localDescription) + ';' + signalEncode(candidates);

	            // Server code logged on console
			    console.log(copiedCode);
				
				return;
			}

			candidates.push(ev.candidate.toJSON());
		};

}

// Handle connection with the PionWebRTC client
/*hostAndCandidatesCode argument is the code provided by our client after submiting the server code in it */
async function ConnectToClientWebRTC(hostAndCandidatesCode: string) {
	try {
		const [hostCode, candidatesCode] = hostAndCandidatesCode.split(';');

		const answer: RTCSessionDescription = signalDecode(hostCode);

		const candidates: RTCIceCandidateInit[] = signalDecode(candidatesCode);

		await peerConnection.setRemoteDescription(answer);

		candidates.forEach(async (candidate) => {
			await peerConnection.addIceCandidate(candidate);
		});
	} catch (e) {
		console.error(e);
	}
}

// Execute code in base of the peerconection state change
function handleConnectionState() {

	const connectionState = peerConnection.connectionState;

	switch (connectionState) {
		case 'connected':
			console.log("connected");
			break;
		case 'disconnected':
			console.log("disconnected");
			ClosePeerConnection();
			break;
		case 'failed':
			console.log("failed");
			ClosePeerConnection();
			break;
		case 'closed':
			console.log("closed");
			ClosePeerConnection();
			break;
	}
}
```

And now we will export our main functions to use in your own codebase

```ts

export { CreateServerWebRTC, ConnectToClientWebRTC, ClosePeerConnection };

```

But now you may have thinked about signalEncode and signalDecode functions, these are some WASM functions generated from the Go codebase to compress the resulting codes and exchange more info like candidates.

You will have all the files on [this gist](https://gist.github.com/PiterWeb/47e77e9011fab02af5c8778569c7686b), now let's create the helper functions to use it out of the box

```ts
// Function WASM (GOLANG)
function signalEncode<T>(signal: T): string {
	// eslint-disable-next-line @typescript-eslint/ban-ts-comment
	//@ts-ignore
	return window.signalEncode(JSON.stringify(signal));
}

// Function WASM (GOLANG)
function signalDecode<T>(signal: string): T {
	// eslint-disable-next-line @typescript-eslint/ban-ts-comment
	//@ts-ignore
	return JSON.parse(window.signalDecode(signal)) as T;
}

```

Now with all our setup we will have the tooling to make an APP were a server (js in the browser) can exchange some numbers and objects with a client (golang with Pion) using a P2P (WebRTC connection) obviously passing the codes manually. But making a service to avoid this it is an easy task, but not what i wanted for my use case (avoid using third party servers/services).

I hope this little tutorial/showcase was helpfull for all the devs who want to experiment with the WebRTC APIs without shooting himself on the foot like i did during the proccess of building **"Remote Controller"** 😉

If you want me to continue this serie of articles talking about how i am building my personal project (like Gamepad emulation) leave one comment on the comments box below 👇