docs: function Client (opts) {
    var self = this
    if (!(self instanceof Client)) return new Client(opts)
    EventEmitter.call(self)
    if (!opts) opts = {}
  
    if (!opts.peerId) throw new Error('Option `peerId` is required')
    if (!opts.infoHash) throw new Error('Option `infoHash` is required')
    if (!opts.announce) throw new Error('Option `announce` is required')
    if (!process.browser && !opts.port) throw new Error('Option `port` is required')
  
    self.peerId = typeof opts.peerId === 'string'
      ? opts.peerId
      : opts.peerId.toString('hex')
    self._peerIdBuffer = Buffer.from(self.peerId, 'hex')
    self._peerIdBinary = self._peerIdBuffer.toString('binary')
  
    self.infoHash = typeof opts.infoHash === 'string'
      ? opts.infoHash
      : opts.infoHash.toString('hex')
    self._infoHashBuffer = Buffer.from(self.infoHash, 'hex')
    self._infoHashBinary = self._infoHashBuffer.toString('binary')
  
    debug('new client %s', self.infoHash)
  
    self.destroyed = false
  
    self._port = opts.port
    self._getAnnounceOpts = opts.getAnnounceOpts
    self._rtcConfig = opts.rtcConfig
    self._userAgent = opts.userAgent
  
    // Support lazy 'wrtc' module initialization
    // See: https://github.com/webtorrent/webtorrent-hybrid/issues/46
    self._wrtc = typeof opts.wrtc === 'function' ? opts.wrtc() : opts.wrtc
  
    var announce = typeof opts.announce === 'string'
      ? [ opts.announce ]
      : opts.announce == null ? [] : opts.announce
  
    // Remove trailing slash from trackers to catch duplicates
    announce = announce.map(function (announceUrl) {
      announceUrl = announceUrl.toString()
      if (announceUrl[announceUrl.length - 1] === '/') {
        announceUrl = announceUrl.substring(0, announceUrl.length - 1)
      }
      return announceUrl
    })
    announce = uniq(announce)
  
    var webrtcSupport = self._wrtc !== false && (!!self._wrtc || Peer.WEBRTC_SUPPORT)
  
    self._trackers = announce
      .map(function (announceUrl) {
        var protocol = url.parse(announceUrl).protocol
        if ((protocol === 'http:' || protocol === 'https:') &&
            typeof HTTPTracker === 'function') {
          return new HTTPTracker(self, announceUrl)
        } else if (protocol === 'udp:' && typeof UDPTracker === 'function') {
          return new UDPTracker(self, announceUrl)
        } else if ((protocol === 'ws:' || protocol === 'wss:') && webrtcSupport) {
          // Skip ws:// trackers on https:// sites because they throw SecurityError
          if (protocol === 'ws:' && typeof window !== 'undefined' &&
              window.location.protocol === 'https:') {
            nextTickWarn(new Error('Unsupported tracker protocol: ' + announceUrl))
            return null
          }
          return new WebSocketTracker(self, announceUrl)
        } else {
          nextTickWarn(new Error('Unsupported tracker protocol: ' + announceUrl))
          return null
        }
      })
      .filter(Boolean)
  
    function nextTickWarn (err) {
      process.nextTick(function () {
        self.emit('warning', err)
      })
    }
  }