Connect to & control your camera with TypeScript
Note: Link is in alpha and APIs may change without backwards compatibility.
This library is a comprehensive TypeScript implementation of ISO-15740:2013 which most camera manufacturers from the last 2 decades have used under the hood to accept commands and transmit information. It also contains a partial implementation of various vendor specifications. libgphoto2 and its command line tool gphoto2 also use these libraries under the hood.
- π Zero Configuration - Automatic camera detection and vendor-specific features
- π¦ 55kB Bundled - Lightweight and tree-shakable
- π₯ 1 major dependency - just
usbfor Node.js - π Runs anywhere - Works in both browser & Node.js
- π― Pure TypeScript - Full type safety and modern DX
- β¨ Simple API - Connect and control your camera with minimal code
- π· Vendor Extensions - Extended features for Sony, Nikon & Canon
- Sony βΊ Series - Live view, video recording, SDIO operations
- Nikon Z Series - Live view, extended properties
- Canon EOS R Series - Remote control, event polling
npm install @darkgrade/linkimport { Camera } from '@darkgrade/link'
const camera = new Camera()
await camera.connect()
// Control camera settings
await camera.setIso('800')
await camera.setShutterSpeed('1/250')
await camera.setAperture('f/2.8')
// Capture an image
const { data } = await camera.captureImage()
await camera.disconnect()// Get current settings
const currentIso = await camera.getIso()
const currentShutter = await camera.getShutterSpeed()
const currentAperture = await camera.getAperture()
// Set new values
await camera.setIso('1600')
await camera.setShutterSpeed('1/500')
await camera.setAperture('f/4.0')import { Camera } from '@darkgrade/link'
const camera = new Camera()
await camera.connect()
// Listen for camera events
camera.on(camera.getInstance().registry.events.ObjectAdded, event => {
console.log('New object added:', event.ObjectHandle)
})
camera.on(camera.getInstance().registry.events.PropertyChanged, event => {
console.log('Property changed:', event.PropertyName)
})
// Remove event listeners
camera.off(camera.getInstance().registry.events.ObjectAdded)// Capture live view frame (Sony & Nikon only)
const { data: liveViewFrame } = await camera.captureLiveView()
// Save or display the frame
fs.writeFileSync('liveview.jpg', liveViewFrame)// Start recording (Sony & Canon only)
await camera.startRecording()
// ... record for some duration ...
// Stop recording
await camera.stopRecording()// List all objects on camera
const objects = await camera.listObjects()
for (const [storageId, storage] of Object.entries(objects)) {
console.log(`Storage ${storageId}: ${storage.info.storageDescription}`)
for (const [handle, info] of Object.entries(storage.objects)) {
console.log(` - ${info.filename} (${info.objectCompressedSize} bytes)`)
// Download a specific object
const fileData = await camera.getObject(Number(handle), info.objectCompressedSize)
fs.writeFileSync(info.filename, fileData)
}
}// Access vendor-specific properties directly
const registry = camera.getInstance().registry
// Get property descriptor
const propValue = await camera.get(registry.properties.ExposureIndex)
// Set property with type safety
await camera.set(registry.properties.ExposureIndex, '3200')The Camera class automatically detects your connected camera's brand and uses the appropriate vendor-specific implementation:
- Sony Ξ± Series β Automatically uses
SonyCamerawith Sony extensions - Nikon Z Series β Automatically uses
NikonCamerawith Nikon extensions - Canon EOS R Series β Automatically uses
CanonCamerawith Canon extensions - Other PTP Cameras β Falls back to
GenericCamerawith standard PTP operations
You can also import and use vendor-specific camera classes directly:
import { SonyCamera } from '@darkgrade/link'
// or NikonCamera, CanonCamera, GenericCameraOr specify a device descriptor when initializing the Camera constructor:
import { Camera, VendorIDs } from '@darkgrade/link'
// Specify a camera brand for vendor-specific features
const camera = new Camera({
device: {
usb: {
filters: [{ vendorId: VendorIDs.SONY }], // VendorIDs.NIKON, VendorIDs.CANON
},
},
logger: {
expanded: true, // Show detailed logging
},
})
await camera.connect()| Feature | Generic PTP | Sony | Nikon | Canon |
|---|---|---|---|---|
| Connection | β | β | β | β |
| Get/Set Properties | β | β | β | β |
| Event Handling | β | β | β | β |
| Aperture Control | β | β | β | β |
| Shutter Speed Control | β | β | β | β |
| ISO Control | β | β | β | β |
| Capture Image | β | β | β | β |
| List Objects | β | β | β | π‘ |
| Download Objects | β | β | β | π‘ |
| Live View | β 1 | β | β | π‘ |
| Video Recording | β 2 | β | β 3 | π‘ |
| Tested with: | Ξ±6700 Ξ±7 IV Ξ±7 V |
Z6 III | EOS R6 Mk.III |
Notes
- The earliest versions of PTP date back to 2002 and this was not included in the specification (perhaps not thought of as necessary/useful/possible on the first wave of digital still cameras).
- Same as (1) above
- Nikon cameras differentiate between "photo mode" and "video mode" with an on-camera hardware switch and do not typically allow capture of (a) videos while in photo mode or (b) photos while in video mode. There are two workarounds we support:
- You accept this limitation and get full feature support for photo OR video, but not both at the same time, via the hardware switch. This is optimal if you don't plan to do hybrid shooting within the same session.
- We allow you to do both at the same time in either switch mode, however when you are capturing in the "wrong" mode" (e.g. you start recording a video while in photo mode), the on-screen display on your camera will be blank and say "Connected to Computer."
ISO 15740:2013 - PTP specification
made with β€οΈ by darkgrade