// For interactive documentation and code auto-completion in editor/** @typedef{import('pear-interface')} *//* global Pear */import Hyperswarm from'hyperswarm'// Module for P2P networking and connecting peersimport crypto from'hypercore-crypto'// Cryptographic functions for generating the key in appimport b4a from'b4a'// Module for buffer-to-string and vice-versa conversions const { teardown,updates } = Pear // Functions for cleanup and updatesconstswarm=newHyperswarm()// Unannounce the public key before exiting the process// (This is not a requirement, but it helps avoid DHT pollution)teardown(() =>swarm.destroy())// Enable automatic reloading for the app// This is optional but helpful during productionupdates(() =>Pear.reload())// When there's a new connection, listen for new messages, and add them to the UIswarm.on('connection', (peer) => {// name incoming peers after first 6 chars of its public key as hexconstname=b4a.toString(peer.remotePublicKey,'hex').substr(0,6)peer.on('data', message =>onMessageAdded(name, message))peer.on('error', e =>console.log(`Connection error: ${e}`))})// When there's updates to the swarm, update the peers countswarm.on('update', () => {document.querySelector('#peers-count').textContent =swarm.connections.size})document.querySelector('#create-chat-room').addEventListener('click', createChatRoom)document.querySelector('#join-form').addEventListener('submit', joinChatRoom)document.querySelector('#message-form').addEventListener('submit', sendMessage)asyncfunctioncreateChatRoom() {// Generate a new random topic (32 byte string)consttopicBuffer=crypto.randomBytes(32)joinSwarm(topicBuffer)}asyncfunctionjoinChatRoom (e) {e.preventDefault()consttopicStr=document.querySelector('#join-chat-room-topic').valueconsttopicBuffer=b4a.from(topicStr,'hex')joinSwarm(topicBuffer)}asyncfunctionjoinSwarm (topicBuffer) {document.querySelector('#setup').classList.add('hidden')document.querySelector('#loading').classList.remove('hidden')// Join the swarm with the topic. Setting both client/server to true means that this app can act as both.constdiscovery=swarm.join(topicBuffer, { client:true, server:true })awaitdiscovery.flushed()consttopic=b4a.toString(topicBuffer,'hex')document.querySelector('#chat-room-topic').innerText = topicdocument.querySelector('#loading').classList.add('hidden')document.querySelector('#chat').classList.remove('hidden')}functionsendMessage (e) {constmessage=document.querySelector('#message').valuedocument.querySelector('#message').value =''e.preventDefault()onMessageAdded('You', message)// Send the message to all peers (that you are connected to)constpeers= [...swarm.connections]for (constpeerof peers) peer.write(message)}// appends element to #messages element with content set to sender and messagefunctiononMessageAdded (from, message) {const$div=document.createElement('div')$div.textContent =`<${from}> ${message}`document.querySelector('#messages').appendChild($div)}
Note that the pear dependency is used, even though it was not installed. This is the Pear API, available to any Pear project.
Step 4. Chat
Open two app instances by running pear run --dev . in two terminals.
In the first app, click on Create. A random topic will appear at the top.
Note that topics consist of 64 hexadecimal characters (32 bytes).
Paste the topic into the second app, then click on Join.
Once connected, messages can be sent between each chat application.
Discussion
Chatting With Another Machine
The two application instances used Hyperswarm's distributed hash table (DHT) to connect with each other.
The DHT enables connections across different machines, so chatting with other people is also possible, as long as they run the same application.
One option is to copy the code, but it is also possible to distribute the application itself over the DHT. This is the topic of Sharing a Pear Application.
Joining Topics VS Joining Servers
In a traditional client-server setup, the server is hosted at an IP address (or hostname) and a port, e.g. http://localhost:3000. This is what clients use to connect to the server.
The code in app.js contains the line swarm.join(topicBuffer, { client: true, server: true }).
topicBuffer is the invitation: anyone who knows this topic can join the room and message the other members.
Note that all members are equal: there is no separate client or server. If the room creator goes offline, or even deletes the room from their machine, the other members can continue chatting.