JavaScript/TypeScript library built on top of WebUSB for controlling digital cameras from browsers.
Alternatives To Tethr
Select To Compare




Tethr is a JavaScript/TypeScript library for controlling USB-connected digital cameras from browsers.

It is built on the top of WebUSB API and aims to support cameras from various vendors. There is a protocol called PTP (Picture Transfer Protocol), which provides a way to access cameras' functionalities such as shutter, aperture, ISO and so on via USB or TCP/IP. However, most of cameras adopt their own vendor extensions to communitate with PC and there's hardly any compatability between them. Tethr also aims to fill the gap and provide an uniform and modern interface for developers without worrying about the difference. The project is deeply inspired by libgphoto2.

Installation on Your Package

npm install https://github.com/baku89/tethr
yarn add https://github.com/baku89/tethr

Supported Cameras

As mentioned above, most vendors add their own extension to PTP. Thus it requires vendor-specific support to fully access all of each camera model's features, otherwise the library can access limited number of configurations exposed as a standard device prop defined in PTP specification. The library also supports web camera fallback in the case WebUSB is disabled or no camera is connected such as smartphone.

Here's the list of camera models

Vendor Camera Features
Panasonic Lumix S5 Shutter, LV, AF, MF, Config
Sigma fp Shutter, LV, AF, Config
Ricoh Theta S Shutter, Config
WebCam Shutter, LV, Config

** LV: Liveview, AF: Auto Focus, MF: Manual Focus

Sample Code

This project is on very early stage of development and not yet documented. Here is the sample code for you to grasp how to use the library. Note that all of camera operations have done asyncronically and Tethr's instance methods return Promise.

import {detectTethr} from 'tethr'

const cameras = await detectTethr()

const cam = cameras[0]

await cam.init()

await cam.get('model') // 'Lumix S5'
await cam.getModel()

await cam.set('shutterSpeed', '1/1000')
await cam.setShutterSpeed('1/1000')

const exposureModeDesc = await cam.getDesc('exposureMode')
/* -> {
	value: 'M',
	writable: false // Because this can be set by physical dial on a camera
	option: {
		type: 'enum',
		values: ['M', 'S', 'A', 'P']
} */

const autoFocusResult = await cam.runAutoFocus()

if (!autoFocusResult.status !== 'ok') {
	console.warn('AF failed')

const takePhotoResult = await cam.takePhoto({download: true})

if (takePhotoResult.status === 'ok') {
	const url = URL.createURLObject(takePhotoResult.value[0])
	$img.src = url

// Get storage informations
const storages = await cam.getStorages()

for (const storage of storages) {
	console.log('Storage ID: ' + storage.id)
	console.log('name=' + storage.name)
	console.log('free space in images=' + storage.freeSpaceInImages)

await cam.close()


This is a list of ConfigName and its value type:

ConfigName Type Example
aperture Apertue 2.2, 5.6, 'auto'
batteryLevel BatteryLevel 50, 100, 'ac', 'low' (Represented in range 0-100)
burstInterval number
burstNumber number
canRunAutoFocus boolean
canRunManualFocus boolean
canStartLiveview boolean
canTakePicture boolean
captureDelay number
colorMode string V-Log, Teal and Orange, CineV... (vendor-specific)
colorTemperature number 2600, 5500
contrast number
dateTime Date
destinationToSave string 'camera', 'pc', 'camera,pc'
digitalZoom number
driveMode DriveMode 'normal', 'burst', 'interval'
exposureComp string '-1 1/3' '-1/2', '0', '+2 1/3'
exposureMeteringMode ExposureMeteringMode 'average', 'multi-spot', 'center-spot'...
exposureMode ExposureMode 'P', 'A', 'S', 'M'
facingMode string 'user', 'environemnt'... (Webcam fallback only)
flashMode FlashMode 'auto', 'off', 'fill'...
focalLength FocalLength 35, 55, 105, 'spherical' (= Theta S, Insta360)
focusDistance number
focusMeteringMode FocusMeteringMode 'center-spot', 'multi-spot'
focusMode FocusMode
functionalMode FunctionalMode 'standard', 'sleep'
imageAspect string '16:9', '3:2', 'a size'
imageQuality string 'fine', 'raw,fine', 'raw' (comma-separated)
imageSize string 'L', 'M', 'S', '1024x768'
iso ISO 160, 3200, 'auto'
liveviewEnabled boolean
liveviewMagnifyRatio number
liveviewSize string
manualFocusOptions ManualFocusOption[] ['near:2', 'near:1', 'far:1', 'far:2'] (3 at max speed)
model string
sharpness number
shutterSpeed string '30', '1.5', '1/200', '1/6400'
timelapseInterval number
timelapseNumber number
whiteBalance WhiteBalance 'auto', 'cloud', 'daylight'...


You can retrieve and modify configs by two ways shown below:

// 1. Specify a kind of config as argument
tethr.get('<configName>'): Promise<ConfigType | null>
tethr.set('<configName>'): Promise<Result>

// 1. Directly call a getter/setter methods defined per config
tethr.get<ConfigName>(): Promise<ConfigType | null>
tethr.set<ConfigName>(): Promise<Result>

interface Result {
	status: 'ok' | 'unsupported' | 'invalid parameter' | 'busy' | 'general error'

// '<configName>' is a name of config written in camelCase ('batteryLevel'),
// and <ConfigName> in CapitalCase (BatteryLevel)

Config Discriptor

If you also want to know an information of config such as writability and valid options, you can use tethr.getDesc('<configName>') or tethr.getConfigNameDesc methods. These return descriptor object.

interface ConfigDesc<ConfigType> {
	writable: boolean
	value: ConfigType | null
		| {type: 'enum'; values: ConfigType[]}
		| {type: 'range'; min: ConfigType; max: ConfigType; step: ConfigType}

Watching Config Changes

Whenever a value of configuration is changed, correspoinding ${configName}Changed event will be fired. And since Tethr class inherits from EventEmitter, you can monitor the value change as follows:

function callback(desc: ConfigDesc<Aperture>) {
	console.log(`Current aperture=${desc.value}`)

// Register the callback
tethr.on('apertureChanged', callback)

// Or watch once
tethr.once('shutterSpeedChanged', callback)

// Delete event listener
tethr.off('apertureChanged', callback)

A event ${configName}Changed is sent out in the case:

  • When you manually set the value of config itself.
  • When you modify other configs which will affect the config value as a side effect
    (Setting 'whiteBalance' to 'auto' makes 'colorTemperature' readonly, for instance)
  • When users change settings by camera buttons / dials physically.

Development Environment

git clone https://github.com/baku89/tethr tethr
cd tethr
yarn install
yarn dev # Then open https://localhost:1234 with a browser
Popular Hardware Categories
Related Searches

Get A Weekly Email With Trending Projects For These Categories
No Spam. Unsubscribe easily at any time.