{"id":13406543,"url":"https://github.com/livekit/client-sdk-js","last_synced_at":"2026-04-16T03:01:04.431Z","repository":{"id":36952064,"uuid":"309592063","full_name":"livekit/client-sdk-js","owner":"livekit","description":"LiveKit browser client SDK (javascript)","archived":false,"fork":false,"pushed_at":"2026-04-14T14:04:44.000Z","size":9126,"stargazers_count":612,"open_issues_count":74,"forks_count":267,"subscribers_count":27,"default_branch":"main","last_synced_at":"2026-04-14T15:34:20.432Z","etag":null,"topics":["javascript","typescript","webrtc"],"latest_commit_sha":null,"homepage":"https://livekit.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/livekit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-11-03T06:15:40.000Z","updated_at":"2026-04-14T13:16:12.000Z","dependencies_parsed_at":"2023-12-13T12:26:52.939Z","dependency_job_id":"0961e2bc-3566-48fe-8837-448548b7d7b5","html_url":"https://github.com/livekit/client-sdk-js","commit_stats":{"total_commits":889,"total_committers":39,"mean_commits":"22.794871794871796","dds":0.6681664791901012,"last_synced_commit":"b6192e0e90ecb3891e62654499bee2649573fd2a"},"previous_names":[],"tags_count":285,"template":false,"template_full_name":null,"purl":"pkg:github/livekit/client-sdk-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/livekit","download_url":"https://codeload.github.com/livekit/client-sdk-js/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-js/sbom","scorecard":{"id":571312,"data":{"date":"2025-08-11","repo":{"name":"github.com/livekit/client-sdk-js","commit":"75517b823782076c5b80100afa51b810d576607e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5.4,"checks":[{"name":"Maintained","score":10,"reason":"30 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/release.yaml:1","Warn: no topLevel permission defined: .github/workflows/size-limit.yaml:1","Warn: no topLevel permission defined: .github/workflows/test.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yaml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/release.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yaml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/release.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yaml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/release.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yaml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/release.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/size-limit.yaml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/size-limit.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/size-limit.yaml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/size-limit.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/size-limit.yaml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/size-limit.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/size-limit.yaml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/size-limit.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/test.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yaml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/test.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yaml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/livekit/client-sdk-js/test.yaml/main?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   5 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":5,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Warn: codeowners review is not required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":9,"reason":"SAST tool is not run on all commits -- score normalized to 9","details":["Warn: 29 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"15 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T16:25:41.963Z","repository_id":36952064,"created_at":"2025-08-20T16:25:41.963Z","updated_at":"2025-08-20T16:25:41.963Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31869050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"online","status_checked_at":"2026-04-16T02:00:06.042Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","typescript","webrtc"],"created_at":"2024-07-30T19:02:33.178Z","updated_at":"2026-04-16T03:01:04.421Z","avatar_url":"https://github.com/livekit.png","language":"TypeScript","readme":"\u003c!--BEGIN_BANNER_IMAGE--\u003e\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"/.github/banner_dark.png\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"/.github/banner_light.png\"\u003e\n  \u003cimg style=\"width:100%;\" alt=\"The LiveKit icon, the name of the repository and some sample code in the background.\" src=\"https://raw.githubusercontent.com/livekit/client-sdk-js/main/.github/banner_light.png\"\u003e\n\u003c/picture\u003e\n\n\u003c!--END_BANNER_IMAGE--\u003e\n\n# JavaScript/TypeScript client SDK for LiveKit\n\n\u003c!--BEGIN_DESCRIPTION--\u003e\nUse this SDK to add realtime video, audio and data features to your JavaScript/TypeScript app. By connecting to \u003ca href=\"https://livekit.io/\"\u003eLiveKit\u003c/a\u003e Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with just a few lines of code.\n\u003c!--END_DESCRIPTION--\u003e\n\n## Docs\n\nDocs and guides at [https://docs.livekit.io](https://docs.livekit.io)\n\n[SDK reference](https://docs.livekit.io/client-sdk-js/)\n\n\u003e [!NOTE]\n\u003e This is v2 of `livekit-client`. When migrating from v1.x to v2.x you might encounter a small set of breaking changes.\n\u003e Read the [migration guide](https://docs.livekit.io/recipes/migrate-from-v1/) for a detailed overview of what has changed.\n\n## Installation\n\n### Yarn\n\n```shell\nyarn add livekit-client\n```\n\n### NPM\n\n```shell\nnpm install livekit-client --save\n```\n\n### Minified JS\n\nTo use the SDK without a package manager, you can include it with a script tag:\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js\"\u003e\u003c/script\u003e\n```\n\nThe module will be exported under `LivekitClient` in the global namespace. When\naccessing symbols from the class, you'd need to prefix them with `LivekitClient.`.\nFor example, `Room` becomes `LivekitClient.Room`.\n\n## Usage\n\nExamples below are in TypeScript, if using JS/CommonJS imports replace import with:\n\n```javascript\nconst livekit = require('livekit-client');\n\nconst room = new livekit.Room(...);\n\n// call this some time before actually connecting to speed up the actual connection\nroom.prepareConnection(url, token);\n\nawait room.connect(...);\n```\n\n### Connecting to a room, publish video \u0026 audio\n\n```typescript\nimport {\n  LocalParticipant,\n  LocalTrackPublication,\n  Participant,\n  RemoteParticipant,\n  RemoteTrack,\n  RemoteTrackPublication,\n  Room,\n  RoomEvent,\n  Track,\n  VideoPresets,\n} from 'livekit-client';\n\n// creates a new room with options\nconst room = new Room({\n  // automatically manage subscribed video quality\n  adaptiveStream: true,\n\n  // optimize publishing bandwidth and CPU for published tracks\n  dynacast: true,\n\n  // default capture settings\n  videoCaptureDefaults: {\n    resolution: VideoPresets.h720.resolution,\n  },\n});\n\n// get your url from livekit's dashboard, or point it at a self hosted livekit deployment\nconst url = \"ws://localhost:7800\";\n\n// generate a token by making a request to a endpoint using the livekit server sdk or\n// using a prebuilt TokenSource (documented below)\nconst token = \"...\";\n\n// pre-warm connection, this can be called as early as your page is loaded\nroom.prepareConnection(url, token);\n\n// set up event listeners\nroom\n  .on(RoomEvent.TrackSubscribed, handleTrackSubscribed)\n  .on(RoomEvent.TrackUnsubscribed, handleTrackUnsubscribed)\n  .on(RoomEvent.ActiveSpeakersChanged, handleActiveSpeakerChange)\n  .on(RoomEvent.Disconnected, handleDisconnect)\n  .on(RoomEvent.LocalTrackUnpublished, handleLocalTrackUnpublished);\n\n// connect to room\nawait room.connect(url, token);\nconsole.log('connected to room', room.name);\n\n// publish local camera and mic tracks\nawait room.localParticipant.enableCameraAndMicrophone();\n\nfunction handleTrackSubscribed(\n  track: RemoteTrack,\n  publication: RemoteTrackPublication,\n  participant: RemoteParticipant,\n) {\n  if (track.kind === Track.Kind.Video || track.kind === Track.Kind.Audio) {\n    // attach it to a new HTMLVideoElement or HTMLAudioElement\n    const element = track.attach();\n    parentElement.appendChild(element);\n  }\n}\n\nfunction handleTrackUnsubscribed(\n  track: RemoteTrack,\n  publication: RemoteTrackPublication,\n  participant: RemoteParticipant,\n) {\n  // remove tracks from all attached elements\n  track.detach();\n}\n\nfunction handleLocalTrackUnpublished(\n  publication: LocalTrackPublication,\n  participant: LocalParticipant,\n) {\n  // when local tracks are ended, update UI to remove them from rendering\n  publication.track.detach();\n}\n\nfunction handleActiveSpeakerChange(speakers: Participant[]) {\n  // show UI indicators when participant is speaking\n}\n\nfunction handleDisconnect() {\n  console.log('disconnected from room');\n}\n```\n\nIn order to connect to a room, you need to first create an access token.\n\nSee [authentication docs](https://docs.livekit.io/home/get-started/authentication/) for details\n\n### Handling common track types\n\nWhile LiveKit is designed to be flexible, we've added a few shortcuts that makes working with common track types simple. For a user's camera, microphone, and screen share, you can enable them with the following `LocalParticipant` methods:\n\n```typescript\nconst p = room.localParticipant;\n// turn on the local user's camera and mic, this may trigger a browser prompt\n// to ensure permissions are granted\nawait p.setCameraEnabled(true);\nawait p.setMicrophoneEnabled(true);\n\n// start sharing the user's screen, this will trigger a browser prompt to select\n// the screen to share.\nawait p.setScreenShareEnabled(true);\n\n// disable camera to mute them, when muted, the user's camera indicator will be turned off\nawait p.setCameraEnabled(false);\n```\n\nSimilarly, you can access these common track types on the other participants' end.\n\n```typescript\n// get a RemoteParticipant by their identity\nconst p = room.remoteParticipants.get('participant-identity');\nif (p) {\n  // if the other user has enabled their camera, attach it to a new HTMLVideoElement\n  if (p.isCameraEnabled) {\n    const publication = p.getTrackPublication(Track.Source.Camera);\n    if (publication?.isSubscribed) {\n      const videoElement = publication.videoTrack?.attach();\n      // do something with the element\n    }\n  }\n}\n```\n\n### Creating a track prior to creating a room\n\nIn some cases, it may be useful to create a track before creating a room. For\nexample, when building a staging area so the user may check their own camera.\n\nYou can use our global track creation functions for this:\n\n```typescript\nconst tracks = await createLocalTracks({\n  audio: true,\n  video: true,\n});\n```\n\n### Publish tracks from any source\n\nLiveKit lets you publish any track as long as it can be represented by a MediaStreamTrack. You can specify a name on the track in order to identify it later.\n\n```typescript\nconst pub = await room.localParticipant.publishTrack(mediaStreamTrack, {\n  name: 'mytrack',\n  simulcast: true,\n  // if this should be treated like a camera feed, tag it as such\n  // supported known sources are .Camera, .Microphone, .ScreenShare\n  source: Track.Source.Camera,\n});\n\n// you may mute or unpublish the track later\npub.setMuted(true);\n\nroom.localParticipant.unpublishTrack(mediaStreamTrack);\n```\n\n### Device management APIs\n\nUsers may have multiple input and output devices available. LiveKit will automatically use the one that's deemed as the `default` device on the system. You may also list and specify an alternative device to use.\n\nWe use the same deviceId as one returned by [MediaDevices.enumerateDevices()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/enumerateDevices).\n\n#### Example listing and selecting a camera device\n\n```typescript\n// list all microphone devices\nconst devices = await Room.getLocalDevices('audioinput');\n\n// select last device\nconst device = devices[devices.length - 1];\n\n// in the current room, switch to the selected device and set\n// it as default audioinput in the future.\nawait room.switchActiveDevice('audioinput', device.deviceId);\n```\n\nYou can also switch devices given a constraint. This could be useful on mobile devices to switch to a back-facing camera:\n\n```typescript\nawait videoTrack.restartTrack({\n  facingMode: 'environment',\n});\n```\n\n#### Handling device failures\n\nWhen creating tracks using LiveKit APIs (`connect`, `createLocalTracks`, `setCameraEnabled`, etc), it's possible to encounter errors with the underlying media device. In those cases, LiveKit will emit `RoomEvent.MediaDevicesError`.\n\nYou can use the helper `MediaDeviceFailure.getFailure(error)` to determine specific reason for the error.\n\n- `PermissionDenied` - the user disallowed capturing devices\n- `NotFound` - the particular device isn't available\n- `DeviceInUse` - device is in use by another process (happens on Windows)\n\nThese distinctions enables you to provide more specific messaging to the user.\n\nYou could also retrieve the last error with `LocalParticipant.lastCameraError` and `LocalParticipant.lastMicrophoneError`.\n\n### Audio playback\n\nBrowsers can be restrictive with regards to audio playback that is not initiated by user interaction. What each browser considers as user interaction can vary by vendor (for example, Safari on iOS is very restrictive).\n\nLiveKit will attempt to autoplay all audio tracks when you attach them to audio elements. However, if that fails, we'll notify you via `RoomEvent.AudioPlaybackStatusChanged`. `Room.canPlaybackAudio` will indicate if audio playback is permitted. LiveKit takes an optimistic approach so it's possible for this value to change from `true` to `false` when we encounter a browser error.\n\nIn the case user interaction is required, LiveKit provides `Room.startAudio` to start audio playback. This function must be triggered in an onclick or ontap event handler. In the same session, once audio playback is successful, additional audio tracks can be played without further user interactions.\n\n```typescript\nroom.on(RoomEvent.AudioPlaybackStatusChanged, () =\u003e {\n  if (!room.canPlaybackAudio) {\n    // UI is necessary.\n    ...\n    button.onclick = () =\u003e {\n      // startAudio *must* be called in an click/tap handler.\n      room.startAudio().then(() =\u003e {\n        // successful, UI can be removed now\n        button.remove();\n      });\n    }\n  }\n});\n```\n\n### Configuring logging\n\nThis library uses [loglevel](https://github.com/pimterry/loglevel) for its internal logs. You can change the effective log level with the `logLevel` field in `ConnectOptions`.\nThe method `setLogExtension` allows to hook into the livekit internal logs and send them to some third party logging service\n\n```ts\nsetLogExtension((level: LogLevel, msg: string, context: object) =\u003e {\n  const enhancedContext = { ...context, timeStamp: Date.now() };\n  if (level \u003e= LogLevel.debug) {\n    console.log(level, msg, enhancedContext);\n  }\n});\n```\n\n### Generating a url/token with `TokenSource`\n\nA TokenSource is a pre-implemented way of fetching credentials. Once a `TokenSource` is constructed, call\n`fetch` to generate a new set of credentials.\n\nThere are two types of `TokenSource`'s - fixed and configurable. Configurable token sources can be\npassed options as part of the generation process, allowing you to customize the token that they\ngenerate. Fixed token sources generate static credentials and don't accept parameters that can\neffect the generated token.\n\n```ts\n// Fixed token sources don't take any parameters as part of `fetch`:\nconst fixed: TokenSourceFixed = /* ... */;\nconst fixedResponse = await fixed.fetch();\nroom.connect(fixedResponse.serverUrl, fixedResponse.participantToken);\n\n// Configurable token sources can optionally take parameters to change what is encoded into the token:\nconst configurable: TokenSourceConfigurable = /* ... */;\nconst configurableResponse = await configurable.fetch({ agentName: \"agent to dispatch\" } /* \u003c-- here */);\nroom.connect(configurableResponse.serverUrl, configurableResponse.participantToken);\n```\n\n|Mechanism:   | using pre-generated credentials | via a http request to a url | via fully custom logic |\n|-------------|--|--|--|\n|Fixed        | [`TokenSource.literal`](#tokensourceliteral) | \u0026mdash; | [`TokenSource.literal(async () =\u003e { /* ... */ })`](#tokensourceliteral) |\n|Configurable | \u0026mdash; | [`TokenSource.endpoint`](#tokensourceendpoint) or [`TokenSource.sandboxTokenServer`](#tokensourceendpoint)  | [`TokenSource.custom`](#tokensourcecustom) |\n\n#### TokenSource.Literal\nA fixed token source which returns a static set of credentials or a computed set of credentials\nwith no external input required on each call.\n\nExample:\n```ts\nconst literal1 = TokenSource.literal({ serverUrl: \"ws://localhost:7800\", participantToken: \"...\" });\nawait literal1.fetch() // { serverUrl: \"ws://localhost:7800\", participantToken: \"...\" }\n\nconst literal2 = TokenSource.literal(async () =\u003e ({ serverUrl: \"ws://localhost:7800\", participantToken: \"...\" }));\nawait literal2.fetch() // { serverUrl: \"ws://localhost:7800\", participantToken: \"...\" }\n```\n\n#### TokenSource.Endpoint\nA configurable token source which makes a request to an endpoint to generate credentials. By\ndefault, a `POST` request with a `Content-Type: application/json` header is made, and the request\nbody is expected to follow the [standard token format](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server). If\ncredentials generation is successful, the endpoint returns a 2xx status code with a body following\nthe [standard token response format](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server).\n\nExample:\n```ts\nconst endpoint1 = TokenSource.endpoint(\"http://example.com/credentials-endpoint\");\nawait endpoint1.fetch({ agentName: \"agent to dispatch\" }) // { serverUrl: \"...\", participantToken: \"... token encoding agentName ...\" }\n\nconst endpoint2 = TokenSource.endpoint(\"http://example.com/credentials-endpoint\", {\n  // For all supported options below, see https://developer.mozilla.org/en-US/docs/Web/API/RequestInit\n  method: \"PUT\",\n  headers: {\n    \"X-Custom-Header\": \"custom header value\",\n  },\n});\nawait endpoint2.fetch({ agentName: \"agent to dispatch\" }) // { serverUrl: \"...\", participantToken: \"... token encoding agentName ...\" }\n```\n\n#### TokenSource.SandboxTokenServer\nA configurable token source which makes a request to a\n[sandbox token server endpoint](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server),\na LiveKit-hosted token generation mechanism.\n\nThis token generation mechanism is inherently insecure and should only be used for\nprototyping; do NOT use in production.\n\nOne parameter is required - the sandbox id from the dashboard. This is the `token-server-xxxxxx`\nvalue in `https://token-server-xxxxxx.sandbox.livekit.io`.\n\nExample:\n```ts\nconst sandbox = TokenSource.sandboxTokenServer(\"token-server-xxxxxx\");\nawait sandbox.fetch({ agentName: \"agent to dispatch\" }); // { serverUrl: \"...\", participantToken: \"... token encoding agentName ...\" }\n```\n\n#### TokenSource.Custom\nA fully custom configurable token source that allows you to consume any end application-specific\ntoken generation mechanism. Tokens that are generated are cached and used until they expire or the\noptions passed into `fetch` change.\n\nNote that it is expected that all options passed into `fetch` will always be encoded into the\noutput token. If you'd rather implement a fixed version of this TokenSource, see\n`TokenSource.literal(async () =\u003e { /* ... */ })`.\n\nExample:\n```ts\nconst sandbox = TokenSource.custom(async (options) =\u003e {\n  // generate token info via custom means here\n  return { serverUrl: \"...\", participantToken: \"... options encoded in here ...\" };\n});\nawait sandbox.fetch({ agentName: \"agent to dispatch\" });\n```\n\n### RPC\n\nPerform your own predefined method calls from one participant to another.\n\nThis feature is especially powerful when used with [Agents](https://docs.livekit.io/agents), for instance to forward LLM function calls to your client application.\n\n#### Registering an RPC method\n\nThe participant who implements the method and will receive its calls must first register support:\n\n```typescript\nroom.localParticipant?.registerRpcMethod(\n  // method name - can be any string that makes sense for your application\n  'greet',\n\n  // method handler - will be called when the method is invoked by a RemoteParticipant\n  async (data: RpcInvocationData) =\u003e {\n    console.log(`Received greeting from ${data.callerIdentity}: ${data.payload}`);\n    return `Hello, ${data.callerIdentity}!`;\n  },\n);\n```\n\nIn addition to the payload, your handler will also receive `responseTimeout`, which informs you the maximum time available to return a response. If you are unable to respond in time, the call will result in an error on the caller's side.\n\n#### Performing an RPC request\n\nThe caller may then initiate an RPC call like so:\n\n```typescript\ntry {\n  const response = await room.localParticipant!.performRpc({\n    destinationIdentity: 'recipient-identity',\n    method: 'greet',\n    payload: 'Hello from RPC!',\n  });\n  console.log('RPC response:', response);\n} catch (error) {\n  console.error('RPC call failed:', error);\n}\n```\n\nYou may find it useful to adjust the `responseTimeout` parameter, which indicates the amount of time you will wait for a response. We recommend keeping this value as low as possible while still satisfying the constraints of your application.\n\n#### Errors\n\nLiveKit is a dynamic realtime environment and calls can fail for various reasons.\n\nYou may throw errors of the type `RpcError` with a string `message` in an RPC method handler and they will be received on the caller's side with the message intact. Other errors will not be transmitted and will instead arrive to the caller as `1500` (\"Application Error\"). Other built-in errors are detailed in `RpcError`.\n\n## Error Codes\n\n| Code  | Name                        | Reason             |\n| ----- | --------------------------- | ------------------ |\n| 1     | `ConnectionError`           | 0: `NotAllowed`\u003cbr\u003e1: `ServerUnreachable`\u003cbr\u003e2: `InternalError`\u003cbr\u003e3: `Cancelled`\u003cbr\u003e4:`LeaveRequest` |  \n| 10    | `UnsupportedServer`         |             |\n| 12    | `UnexpectedConnectionState` |             |\n| 13    | `NegotiationError`          |             |\n| 14    | `PublishDataError`          |             |\n| 15    | `SignalRequestError`        |             |\n| 20    | `TrackInvalidError`         |             |\n| 21    | `DeviceUnsupportedError`    |             |\n| 40    | `CryptorError`              |             |\n\n## Examples\n\n### Demo App\n\n[examples/demo](https://github.com/livekit/client-sdk-js/tree/main/examples/demo/) contains a demo webapp that uses the SDK. Run it with `pnpm install \u0026\u0026 pnpm examples:demo`\n\n### RPC Demo\n\n[examples/rpc](https://github.com/livekit/client-sdk-js/tree/main/examples/rpc/) contains a demo webapp that uses the SDK to showcase the RPC capabilities. Run it with `pnpm install \u0026\u0026 pnpm dev` from the `examples/rpc` directory.\n\n## Browser Support\n\n| Browser         | Desktop OS            | Mobile OS |\n| --------------- | --------------------- | --------- |\n| Chrome          | Windows, macOS, Linux | Android   |\n| Firefox         | Windows, macOS, Linux | Android   |\n| Safari          | macOS                 | iOS       |\n| Edge (Chromium) | Windows, macOS        |           |\n\nWe aim to support a broad range of browser versions by transpiling the library code with babel.\nYou can have a look at the `\"browerslist\"` section of `package.json` for more details.\n\n\u003e Note that the library requires some specific browser APIs to be present.\n\u003e You can check general compatibility with the helper function `isBrowserSupported()`.\n\u003e Support for more modern features like adaptiveStream and dynacast can be checked for with `supportsAdaptiveStream()` and `supportsDynacast()`.\n\nIf you are targeting legacy browsers, but still want adaptiveStream functionality you'll likely need to use polyfills for [ResizeObserver](https://www.npmjs.com/package/resize-observer-polyfill) and [IntersectionObserver](https://www.npmjs.com/package/intersection-observer).\n\nAlso when targeting legacy browsers, older than the ones specified in our browserslist target, make sure to transpile the library code to your desired target and include required polyfills with babel and/or corejs.\n\n\u003c!--BEGIN_REPO_NAV--\u003e\n\u003cbr/\u003e\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\u003cth colspan=\"2\"\u003eLiveKit Ecosystem\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\u003ctd\u003eAgents SDKs\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/agents\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/agents-js\"\u003eNode.js\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eLiveKit SDKs\u003c/td\u003e\u003ctd\u003e\u003cb\u003eBrowser\u003c/b\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-swift\"\u003eSwift\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-android\"\u003eAndroid\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-flutter\"\u003eFlutter\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-react-native\"\u003eReact Native\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/rust-sdks\"\u003eRust\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/node-sdks\"\u003eNode.js\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/python-sdks\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-unity\"\u003eUnity\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-unity-web\"\u003eUnity (WebGL)\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-esp32\"\u003eESP32\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-cpp\"\u003eC++\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eStarter Apps\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit-examples/agent-starter-python\"\u003ePython Agent\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-node\"\u003eTypeScript Agent\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-react\"\u003eReact App\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-swift\"\u003eSwiftUI App\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-android\"\u003eAndroid App\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-flutter\"\u003eFlutter App\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-react-native\"\u003eReact Native App\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples/agent-starter-embed\"\u003eWeb Embed\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eUI Components\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/components-js\"\u003eReact\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/components-android\"\u003eAndroid Compose\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/components-swift\"\u003eSwiftUI\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/components-flutter\"\u003eFlutter\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eServer APIs\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/node-sdks\"\u003eNode.js\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-go\"\u003eGolang\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-ruby\"\u003eRuby\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-kotlin\"\u003eJava/Kotlin\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/python-sdks\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/rust-sdks\"\u003eRust\u003c/a\u003e · \u003ca href=\"https://github.com/agence104/livekit-server-sdk-php\"\u003ePHP (community)\u003c/a\u003e · \u003ca href=\"https://github.com/pabloFuente/livekit-server-sdk-dotnet\"\u003e.NET (community)\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eResources\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://docs.livekit.io\"\u003eDocs\u003c/a\u003e · \u003ca href=\"https://docs.livekit.io/mcp\"\u003eDocs MCP Server\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/livekit-cli\"\u003eCLI\u003c/a\u003e · \u003ca href=\"https://cloud.livekit.io\"\u003eLiveKit Cloud\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eLiveKit Server OSS\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/livekit\"\u003eLiveKit server\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/egress\"\u003eEgress\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/ingress\"\u003eIngress\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/sip\"\u003eSIP\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eCommunity\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://community.livekit.io\"\u003eDeveloper Community\u003c/a\u003e · \u003ca href=\"https://livekit.io/join-slack\"\u003eSlack\u003c/a\u003e · \u003ca href=\"https://x.com/livekit\"\u003eX\u003c/a\u003e · \u003ca href=\"https://www.youtube.com/@livekit_io\"\u003eYouTube\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c!--END_REPO_NAV--\u003e\n","funding_links":[],"categories":["TypeScript","Libraries"],"sub_categories":["JavaScript"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flivekit%2Fclient-sdk-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flivekit%2Fclient-sdk-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flivekit%2Fclient-sdk-js/lists"}