A lightweight, flexible JavaScript library for injecting video, photo, and audio capture functionality into any DOM element. Built with vanilla JavaScript, no dependencies required.
- Features
- Installation
- Quick Start
- Configuration Options
- Callbacks
- Public Methods
- Metadata Structure
- UI Events
- Customization with CSS Variables
- Examples
- Asset Files
- Troubleshooting
- Browser Support
- Known Limitations
- Build
- Contributing
- License
- WordPress Integration
- Multiple Capture Modes: Video, photo, and audio recording
- Camera Controls: Switch between front and rear cameras (where available)
- Audio Input Selection: Choose from available microphones
- Countdown Timer: Configurable countdown before capture starts with optional sound effects
- Sound Effects: Support for custom audio files for countdown and shutter sounds
- Pause/Resume: Video and audio pause/resume functionality
- Customizable UI: Fully themeable with CSS variables
- Event Callbacks: Comprehensive callback system for capture events
- Public API: Direct control via public methods
- Download Support: Built-in media download functionality
- Settings Panel: UI for selecting cameras and microphones
- Responsive Design: Mobile-friendly interface
- Browser Compatibility: Works in modern browsers with getUserMedia support
Download the repository and include the files directly:
<link rel="stylesheet" href="dist/media-capture.css">
<script src="dist/media-capture.js"></script>Note: This package is not yet published to npm. Coming soon!
# Not yet available
npm install media-capture<div id="capture-container"></div>
<script>
var container = document.getElementById('capture-container');
var capture = new MediaCapture(container, {
mode: 'video',
auto_start: false,
on_video_captured: function(blob, filename, metadata) {
console.log('Video captured!', filename, metadata);
},
on_error: function(error) {
console.error('Error:', error);
}
});
</script>var capture = new MediaCapture(container, {
mode: 'photo',
on_photo_captured: function(blob, filename, metadata) {
console.log('Photo captured!', filename, metadata);
}
});var capture = new MediaCapture(container, {
mode: 'audio',
max_duration: 30,
on_audio_captured: function(blob, filename, metadata) {
console.log('Audio captured!', filename, metadata);
}
});| Option | Type | Default | Description |
|---|---|---|---|
mode |
string | 'video' |
Capture mode: 'video', 'photo', or 'audio' |
auto_start |
boolean | false |
Automatically start camera/microphone on init |
max_duration |
number | 30 |
Maximum duration in seconds (video/audio only) |
video_format |
string | 'mp4' |
Video output format |
audio_format |
string | 'webm' |
Audio output format |
photo_format |
string | 'jpeg' |
Photo output format ('jpeg' or 'png') |
photo_quality |
number | 0.9 |
Photo compression quality (0-1) |
facing_mode |
string | 'user' |
Camera facing mode: 'user' or 'environment' |
resolution |
string | '' |
Requested resolution (e.g., '1280x720') |
max_width |
string | '' |
Maximum width of capture container (CSS) |
controls_auto_hide |
boolean | false |
Auto-hide controls during recording |
controls_auto_hide_duration |
number | 3000 |
Duration in milliseconds before controls auto-hide |
| Option | Type | Default | Description |
|---|---|---|---|
countdown_enabled |
boolean | false |
Enable countdown timer before capture |
countdown_duration |
number | 3 |
Countdown duration in seconds |
countdown_sound_enabled |
boolean | false |
Play sound during countdown |
countdown_sound_url |
string | '' |
URL to custom countdown sound file |
| Option | Type | Default | Description |
|---|---|---|---|
photo_sound_enabled |
boolean | false |
Play sound when photo is captured |
photo_sound_url |
string | '' |
URL to custom photo/shutter sound file |
| Option | Type | Default | Description |
|---|---|---|---|
settings_download_enabled |
boolean | false |
Show download button in settings panel |
log |
boolean | false |
Enable internal logging |
log_to_console |
boolean | true |
Log to browser console |
All UI labels can be customized. Common labels:
| Label | Default |
|---|---|
label_start_recording |
'Start Recording' |
label_stop |
'Stop' |
label_pause |
'Pause' |
label_resume |
'Resume' |
label_capture_photo |
'Take Photo' |
label_retake |
'Retake Photo' |
label_cancel_retake |
'Cancel' |
label_settings |
'Settings' |
label_flip_camera |
'Flip Camera' |
label_start_camera |
'Start Camera' |
label_start_microphone |
'Start Microphone' |
label_volume |
'Volume' |
label_progress |
'Progress' |
See the library for the complete list of customizable labels.
new MediaCapture(container, {
on_video_captured: function(blob, filename, metadata) {
// Called when video recording completes
},
on_video_started: function() {
// Called when recording starts
},
on_video_stopped: function() {
// Called when recording stops
},
on_video_paused: function() {
// Called when recording is paused
},
on_video_resumed: function() {
// Called when recording resumes
},
on_video_retake: function() {
// Called when retake is initiated
},
on_video_cancel: function() {
// Called when retake is cancelled
}
});new MediaCapture(container, {
on_photo_captured: function(blob, filename, metadata) {
// Called when photo is captured
},
on_photo_retake: function() {
// Called when retake is initiated
},
on_photo_cancel: function() {
// Called when retake is cancelled
}
});new MediaCapture(container, {
on_audio_captured: function(blob, filename, metadata) {
// Called when audio recording completes
},
on_audio_started: function() {
// Called when recording starts
},
on_audio_stopped: function() {
// Called when recording stops
},
on_audio_paused: function() {
// Called when recording is paused
},
on_audio_resumed: function() {
// Called when recording resumes
},
on_audio_retake: function() {
// Called when retake is initiated
},
on_audio_cancel: function() {
// Called when retake is cancelled
}
});new MediaCapture(container, {
on_stream_started: function() {
// Camera/microphone stream started
},
on_stream_stopped: function() {
// Camera/microphone stream stopped
},
on_stream_flipped: function() {
// Camera was flipped
},
on_settings_opened: function() {
// Settings panel opened
},
on_settings_closed: function() {
// Settings panel closed
},
on_result_show: function() {
// Result preview shown
},
on_result_hide: function() {
// Result preview hidden
},
on_cameras_detected: function(cameras) {
// Array of available cameras detected
// cameras is an array of MediaDeviceInfo objects
},
on_error: function(error) {
// Error occurred
},
on_log: function(message, args, level) {
// Custom logging callback
}
});capture.stream_start(); // Start camera/microphone
capture.stream_stop(); // Stop stream
capture.stream_flip(); // Flip camera (front/rear)capture.video_start(); // Start video recording
capture.video_stop(); // Stop video recording
capture.video_pause(); // Pause video recording
capture.video_resume(); // Resume video recording
capture.video_retake(); // Show retake prompt
capture.video_cancel(); // Cancel retakecapture.photo_capture(); // Capture photo
capture.photo_retake(); // Show retake prompt
capture.photo_cancel(); // Cancel retakecapture.audio_start(); // Start audio recording
capture.audio_stop(); // Stop audio recording
capture.audio_pause(); // Pause audio recording
capture.audio_resume(); // Resume audio recording
capture.audio_retake(); // Show retake prompt
capture.audio_cancel(); // Cancel retakecapture.playback_play(); // Play captured video/audio
capture.playback_pause(); // Pause playback
capture.playback_stop(); // Stop playback
capture.playback_toggle(); // Toggle play/pausecapture.is_playing(); // Check if media is playing
capture.has_video(); // Check if video is captured
capture.has_photo(); // Check if photo is captured
capture.has_audio(); // Check if audio is capturedcapture.get_blob(); // Get media blob
capture.get_meta(); // Get media metadata
capture.download(); // Download media filecapture.settings_open(); // Open settings panel
capture.settings_close(); // Close settings panelcapture.get_supported_formats(); // Get browser format support
// Returns: { audio: { webm: true, mp4: false, ... }, video: { webm: true, mp4: true, ... } }When media is captured, callbacks receive a metadata object with details about the captured media:
{
type: 'photo',
width: 1920, // Photo width in pixels
height: 1080, // Photo height in pixels
size: 245678, // File size in bytes
format: 'jpeg', // Photo format
quality: 0.9, // Compression quality
timestamp: 1234567890, // Capture timestamp
facing_mode: 'user' // Camera facing mode used
}{
type: 'video',
size: 1234567, // File size in bytes
format: 'mp4', // Video format
duration: 15.3, // Duration in seconds
timestamp: 1234567890, // Capture timestamp
resolution: '1280x720', // Resolution (if set)
facing_mode: 'user' // Camera facing mode used
}{
type: 'audio',
size: 456789, // File size in bytes
format: 'webm', // Audio format
duration: 23.5, // Duration in seconds
timestamp: 1234567890, // Capture timestamp
mime_type: 'audio/webm;codecs=opus' // Full MIME type
}The library dispatches custom events that can be listened to for UI state changes:
document.addEventListener('mc-ui-start-on', function() {
// Start button enabled
});
document.addEventListener('mc-ui-start-off', function() {
// Start button disabled
});
document.addEventListener('mc-ui-stop-on', function() {
// Stop button enabled
});
document.addEventListener('mc-ui-pause-on', function() {
// Pause button enabled
});
document.addEventListener('mc-ui-resume-on', function() {
// Resume button enabled
});
document.addEventListener('mc-ui-retake-on', function() {
// Retake button enabled
});Customize the appearance using CSS custom properties:
:root {
/* Control colors */
--mc-control-color-background: #f5f5f5;
--mc-control-color: #333;
--mc-control-color-record: #dc3545;
--mc-control-color-level: #28a745;
/* Control sizing */
--mc-control-size: 40px;
--mc-control-gap: 10px;
/* Border styling */
--mc-border: 1px solid #e0e0e0;
--mc-border-radius: 8px;
--mc-border-color-hover: #418FDE;
--mc-border-color-focus: #418FDE;
--mc-box-shadow: 0 2px 8px rgba(0,0,0,0.1);
/* Countdown styling */
--mc-countdown-color: #333;
--mc-countdown-color-background: rgba(0,0,0,0.8);
--mc-countdown-font-size: 120px;
--mc-countdown-font-weight: bold;
/* Settings panel */
--mc-settings-color: #333;
--mc-settings-color-background: #ffffff;
}The repository includes comprehensive examples:
- Default Video - Basic video recording
- Video Events - Programmatic control with external buttons
- Video + Countdown - Countdown timer before recording
- Video + Custom Styling - Customized appearance
- Default Photo - Basic photo capture
- Photo Events - Programmatic control
- Photo + Countdown + Sounds - Countdown and shutter sounds
- Default Audio - Basic audio recording
- Audio Events - Programmatic control
- Audio + Countdown - Countdown before recording
- Audio + Custom Countdown - Custom countdown sound file
See the /examples directory for full implementation details.
Include optional sound effects from /assets/audio/:
beep-1.mp3- Standard beep soundbeep-2.mp3- Short sharp beepbeep-3.mp3- Long sharp beep (suitable for shutter sound)camera-shutter.mp3- Camera shutter sound
Use these with countdown_sound_url and photo_sound_url options.
Problem: "Unable to access camera/microphone" error
Solutions:
- Ensure your site is served over HTTPS (required by browsers for getUserMedia)
- Check browser permissions - user must grant camera/microphone access
- On mobile, check that no other app is using the camera
- Try a different browser - some browsers have stricter policies
- Clear browser cache and reload the page
Problem: Recording doesn't work or files are corrupted
Solutions:
- Use
capture.get_supported_formats()to check browser support - Try a different format - use
video_format: 'webm'if mp4 fails - Some browsers only support specific codecs
- Safari has limited format support - WebM works best
Problem: Camera flip doesn't work on mobile
Solutions:
- Some devices only have one camera
- Ensure proper permissions are granted
- Try reloading the page after granting permissions
Problem: Video recording stops unexpectedly
Solutions:
- Mobile browsers may limit recording duration
- Check available storage space
- Reduce
max_durationfor better reliability - Some browsers pause recording when tab loses focus
Problem: Slow performance or high memory usage
Solutions:
- Use lower resolution settings
- Reduce
photo_qualityfor smaller file sizes - Use minified version (
media-capture.min.js) - Dispose of captured media when no longer needed
- Call
capture.destroy()when removing the instance
Modern browsers require HTTPS for accessing camera/microphone. For local development:
# Use a local HTTPS server
npx http-server -S -C cert.pem -K key.pemOr use localhost which is treated as secure by most browsers.
Requires browsers with support for:
getUserMediaAPIMediaRecorderAPIBlobAPI- Promise support
Works in:
- Chrome 49+
- Firefox 29+
- Safari 14.1+
- Edge 15+
- Opera 36+
Not supported in Internet Explorer.
- MediaRecorder API Required: This library depends on the MediaRecorder API, which is not available in older browsers
- Format Support Varies: Different browsers support different video/audio codecs. Use
get_supported_formats()to check - Mobile Safari: Limited format support - prefer WebM for video, may fall back to different codecs
- iOS Limitations: Some iOS versions have restrictions on camera access and recording duration
- No Video Editing: Library captures raw video/audio/photo - no built-in editing capabilities
- File Size: Large recordings may cause memory issues - monitor blob sizes for long recordings
- No Streaming: Captures to Blob in memory - not designed for live streaming
- Single Instance: While multiple instances are supported, only one camera stream per device at a time
- Background Recording: Some mobile browsers pause recording when tab loses focus
- Camera Availability: Flip camera only works on devices with multiple cameras
- Permissions: Browser will prompt for permissions - cannot be bypassed programmatically
- HTTPS Required: Camera/microphone access requires secure context (HTTPS or localhost)
- Mobile Constraints: Mobile devices may have lower recording quality or duration limits
Most limitations can be addressed by:
- Testing format support with
get_supported_formats() - Setting appropriate
max_durationvalues - Handling errors gracefully with
on_errorcallback - Providing user guidance about permissions
- Using feature detection before initialization
Build the project using the included build script:
npm install
npm run buildWatch for changes during development:
npm run watchContributions are welcome! To contribute:
- Fork the repository on Bitbucket
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes
- Run the build (
npm run build) - Test your changes with the examples
- Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/my-feature) - Create a Pull Request
Please ensure your code:
- Follows the existing code style (snake_case for functions)
- Includes comments for complex logic
- Works across all supported browsers
- Doesn't break existing examples
MIT License
Copyright (c) 2025 Westguard Solutions
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For issues, questions, or feature requests, please use the issue tracker on the Bitbucket repository.
This library is designed to work well with WordPress:
- Self-contained, no external dependencies
- Vanilla JavaScript compatible with WordPress enqueue system
- Can be easily enqueued as a plugin or theme asset
- Use snake_case for function names to follow WordPress conventions
- Compatible with both classic and block editor environments
- Works with custom Gutenberg blocks
- No conflicts with popular WordPress plugins
Example WordPress enqueue:
wp_enqueue_script('media-capture', plugin_dir_url(__FILE__) . 'dist/media-capture.js', array(), '1.0.0', true);
wp_enqueue_style('media-capture', plugin_dir_url(__FILE__) . 'dist/media-capture.css', array(), '1.0.0');