M
← Back to posts
P2p file transfer between browsers using webrtc

P2p file transfer between browsers using webrtc

Published: 7/10/2026

this is actively being updated

so we wanna share a file over webrtc?

now lets start with the things you will need to do this -

1. First of all, the two devices need to be able to discover each other

devices usually don’t expose their private ip over the network as routers use NAT (network address translation) to translate private IP adresses into public facing addresses

so how do we solve this?

we use STUN servers to let a device discover the public IP and port mapping that its NAT has created

2. Now the devices can see their actual ip to connect, how to they actually connect to each other?

Before WebRTC actually handles the peer-to-peer connection, two devices need to exchange some information with each other

3. SO what exactly do they exchange?

it is SDP and ICE candidates.

Device A creates an RTCPeerConnection and generates an SDP offer.

this SDP contains info about how A wants to communicate, what type of media it supports and want to share

A sends this offer, device B receives the offer and creates an SDP answer, and it sends the answer back to device B (we will see everything using code in the end)

at the same time, both devices exchange their ICE candidates, which provide the best pathways over the internet for both devices to exchange media after they connect .

now here is a thing, webRTC doesn’t actually provide a way for them to connect to each other and it isn’t opinionated about how you do it,

you can generate SDP and send it and receive answer using however you want,

you can use HTTP, send it over whatsapp, or even use a pigeon to do this but this defies the purpose

So we use the most suitable method for this purpose which is Websockets,

now don’t confuse between webRTC and webSockets,

we are using webrtc for real time communication and media sharing whereas websocket is just being used as signalling server to transfer SDP and ice candidates

Now lets stop the larp and write some code

  1. first of all we define rtcconfig describing which stun server we gonna use

and we will create a

const rtcConfig = {
  iceServers: [
    { urls: 'stun:stun1.l.google.com:19302' },
    { urls: 'stun:stun2.l.google.com:19302' }
  ]
};
 // create a peer 
const pc = new RTCPeerConnection(rtcConfig);

this initializes our connection object which we will use for further purpose

2. Now lets create data channel which is the most important thing for most of the usecases

we create it by using

//dc = datachannnel 
const dc = pc.createDataChannel("channel");

here “channel” is the name of our datachannel

this createDatachannel method returns a RTCDataChannel object

3. now we need to define what to do with different methods provided by this

RTCDataChannel object

first is .onopen , the open event lets us know that channel is ready for communication , this also has its counterpart , .onclose

channel.onopen = () => {
    console.log("data channel is open");
};
channel.onclose = () => {
    console.log("Connection closed");
};

second is .onmessage , when the data channel gets a message sent by other peer using .send, this event lets us know

channel.send("Hello");

channel.onmessage = (event) => {
    console.log("Received:", event.data);
};

for now we just log the message ,

4. next we define another important method of out connection object which is

.onicecandidate

when webRTC discovers an ice candidate this event fires

pc.onicecandidate = e => console.log(“NEW ICe candidate reprinting SDP+ JSON.stringify(lc.localDescription ) ;

dont worry about this localDescription thing , next step will clear everything

5. This is the most important step for us

we create an offer which is a proposal which we wanna further send to other Peer

this is done using .createOffer() method

so in the next line of code we gonna do 3 things

lc.createOffer()
  .then(o => lc.setLocalDescription(o))
  .then(() => console.log("set successfully"));

the createoffer method returns something like

{
    type: "offer",
    sdp: "v=0\r\n..."
}

next we set the LocalDescription which lets the peerconnection to set this offer as its part of description

after that promise resolves , for now we just log it

also this setlocaldescription triggers webRTC to search for Icecandidates and when found it triggers .onicecandidate method which we defined earlier

untill now we have created a peer A , created offer ,setlocaldescription and so on , now comes the part where you send this offer to another Peer , and as we discussed earlier that it is our choice to choose the medium , and we preffered websocket for signalling

so we send offer using socket on finding icecandidate

pc.onicecandidate = (event) => {

    if (event.candidate) {

        console.log("B found ICE candidate");

        socket.send(JSON.stringify({
            type: "candidate",
            candidate: event.candidate
        }));
    }
};

just for demo you can do this by just copy pasting the sdp directly between two different tabs in your browser

Next for the other peer , the process is pretty much same ,so lets go thorugh it quickly

  1. create a peer connection
const pc2 = new RTCPeerConnection(rtcConfig) ;

2 . now we dont have to create a data channel again instead we recieve it from peer A , so we use .ondatachannel method

pc2.ondatachannel = e => {
  rc.dc = e.channel ;
  rc.dc.onmessage = e => console.log("new message " + e.data);
  rc.dc.onopen = e => console.log("connection openede") ;
}

3. now we set the offer received as the remoteDescription

pc2.setRemoteDescription(offer).then(a => console.log("offer set"));

4. now we create a answer and set it in the localdescription similar to how we did it for offer but before that define the .onincecandidate method as it triggers on setLocalDescription

pc2.setRemoteDescription(message.offer);

and then

pc2.createAnswer().then(a => pc2.setLocalDescription(a)).then(a => console.log("answer created")) ;

now you can set this answer in Peer A using setRemotedescription

pc.setRemoteDescription(answer) ;

AND BAM !!! We Are Done !!!!

the webRTC connection has been established

now you can send messages using .send props and you might think message can be sent through websockets so why this , cuz i want to demonstrate core webRTC api objects and methods for now and wanted to keep it simple you can do much more by using the media ,

Before ending always remember this IMPORTANT distinction

we use websocket for signalling server to exchnage SDP and handle different type of signals messages types Whereas ICE candidates are used to find the best available path to exchange data in Real time over WEBRTC

that all for now

to be continued …..