Skip to content

Getting Started

GitHub

You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:

Terminal window
npx skills add https://github.com/Cap-go/capgo-skills --skill capacitor-plugins

Then use the following prompt:

Use the `capacitor-plugins` skill from `Cap-go/capgo-skills` to install the `@capgo/capacitor-audio-recorder` plugin in my project.

If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:

Terminal window
bun add @capgo/capacitor-audio-recorder
bunx cap sync
import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';

Request the microphone, record, then stop and read the native file URI (or web Blob).

import { Capacitor } from '@capacitor/core';
import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const mic = await CapacitorAudioRecorder.requestPermissions();
if (mic.recordAudio !== 'granted') {
throw new Error('Microphone permission required');
}
await CapacitorAudioRecorder.startRecording({
sampleRate: 44100,
bitRate: 128000,
resetToPlaybackOnStop: true, // iOS only: restore playback-friendly audio session after stop
});
const { uri, blob, duration } = await CapacitorAudioRecorder.stopRecording();
const clipRef = Capacitor.getPlatform() === 'web' ? blob : uri;
console.log('Saved clip', clipRef, duration);

Requires @capgo/capacitor-audio-recorder 8.4.0 or newer from capacitor-audio-recorder PR #35. Merge and release that plugin change before relying on this API in production.

Use format: 'aac' for irreplaceable audio: interviews, compliance notes, medical or legal memos, and any clip where losing the whole file is worse than losing the tail after an abnormal end.

Keep the default format: 'm4a' when you control the lifecycle (short clips, always in foreground, you can tolerate losing a take on crash) or when downstream tools expect .m4a and never .aac.

formatContainerAfter an abnormal end (crash, kill, dead battery)
'm4a' (default)MPEG-4 AAC in .m4aOften unreadable until moov is written at stop()
'aac'ADTS AAC in .aacReadable up to the last complete frame

Encoding settings are the same AAC bit rate and sample rate you already pass to startRecording. Web ignores format and keeps using the browser MediaRecorder.

await CapacitorAudioRecorder.startRecording({
format: 'aac',
sampleRate: 44100,
bitRate: 128000,
});
  • Playback and sharing: .aac (ADTS) is widely supported on mobile but some desktop pipelines expect .m4a. Convert in your backend if reviewers require MPEG-4.
  • File size and seeking: ADTS has no index like MPEG-4. Seeking and duration metadata can be less precise until the file is fully finalized.
  • iOS interruptions: Phone calls and audio session handoffs can leave multiple segment files. On stop, the plugin joins ADTS segments without re-encoding when possible.

Android

  • When MediaRecorder.stop() throws, stopRecording rejects but the file stays on disk. Read error.data.uri and error.data.duration (milliseconds) from the Capacitor rejection to keep the partial clip.
  • File names are recording-yyyyMMdd-HHmmss-<uuid>.m4a or .aac, matching format. The UUID keeps two recordings started in the same second from overwriting each other. Earlier versions used recording-yyyyMMdd-HHmmss.m4a (no UUID, always .m4a).
import type { StopRecordingResult } from '@capgo/capacitor-audio-recorder';
try {
const clip = await CapacitorAudioRecorder.stopRecording();
await upload(clip.uri!, clip.duration);
} catch (error: unknown) {
const data = (error as { data?: Pick<StopRecordingResult, 'uri' | 'duration'> }).data;
if (data?.uri) {
await upload(data.uri, data.duration);
} else {
throw error;
}
}

iOS

  • Interrupted 'aac' recordings are merged into one .aac when joining succeeds.
  • When joining fails (for example disk full), stopRecording can resolve with mergeFailed: true, a fallback uri, and segments: string[] listing every segment in order. For 'aac', do not blindly concatenate raw files: a segment can end with an incomplete ADTS frame, and naive concatenation can break playback of what follows. Copy only complete frames from each segment (the plugin’s join logic does this), or upload each segment separately and stitch server-side. For 'm4a', segments cannot be joined by concatenation; remux them or stitch them server-side.
  • When finalization fails with no usable fallback segment, stopRecording rejects but segment files remain on disk.
const result = await CapacitorAudioRecorder.stopRecording();
if (result.mergeFailed && result.segments?.length) {
console.warn('Join failed, segments on disk', result.segments, result.uri);
}

startRecording now rejects when AVAudioRecorder.record() returns false (for example the mic is in use elsewhere) instead of returning an empty take.

await CapacitorAudioRecorder.startRecording();
// user taps pause during a long note
await CapacitorAudioRecorder.pauseRecording();
await CapacitorAudioRecorder.resumeRecording();
const clip = await CapacitorAudioRecorder.stopRecording();

pauseRecording is available on Android (API 24+), iOS, and Web.

Poll getCurrentAmplitude() on a timer (about 60 to 100 ms) while status is RECORDING. Values are normalized 0 to 1, but the underlying signal differs by platform (peak on Android, average power on iOS, RMS on Web). Apply a small per platform scale if you need visual parity.

const tick = async () => {
const { status } = await CapacitorAudioRecorder.getRecordingStatus();
if (status !== 'RECORDING') return;
const { value } = await CapacitorAudioRecorder.getCurrentAmplitude();
updateMeterUi(value);
};
await CapacitorAudioRecorder.startRecording();
await CapacitorAudioRecorder.cancelRecording(); // no file persisted
  • Web vs native return shape: Android and iOS return uri from stopRecording. Web returns blob (and may omit uri).
  • Crash safety vs default format: only format: 'aac' on Android and iOS keeps audio playable after a hard stop. Default .m4a needs a clean stopRecording to stay readable.
  • Failed stop on Android: handle stopRecording rejections and read error.data for uri and duration instead of assuming the file was deleted.
  • Android file names: do not parse the old recording-yyyyMMdd-HHmmss.m4a pattern. Current files include a UUID and may end in .aac when you opt into format: 'aac'.
  • iOS merge failures: when mergeFailed is true, use segments or the fallback uri from the resolved result.
  • Quiet playback on iOS after recording: set resetToPlaybackOnStop: true in startRecording if users play back the clip inside the same session.
  • Background capture: the plugin does not enable background audio by itself. On iOS, add the Audio background mode when you need recording while locked.
  • Do not spam amplitude: each getCurrentAmplitude call crosses the bridge. Use a UI interval, not a tight loop.

Start recording audio using the device microphone.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
await CapacitorAudioRecorder.startRecording();

Pause the ongoing recording. Only available on Android (API 24+), iOS, and Web.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
await CapacitorAudioRecorder.pauseRecording();

Resume a previously paused recording.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
await CapacitorAudioRecorder.resumeRecording();

Stop the current recording and persist the recorded audio.

On Android, when the recorder cannot stop cleanly, the call is rejected and the recording file is kept on disk. The rejection’s data.uri points to it and data.duration reports elapsed time.

On iOS, when the segments of an interrupted 'aac' recording cannot be joined but a fallback segment exists, the call resolves with mergeFailed and segments. When finalization fails with no fallback segment, the call is rejected and segment files remain on disk.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const result = await CapacitorAudioRecorder.stopRecording();
console.log(result);

Cancel the current recording and discard any captured audio.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
await CapacitorAudioRecorder.cancelRecording();

Retrieve the current recording status.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const result = await CapacitorAudioRecorder.getRecordingStatus();
console.log(result);

Retrieve the current input amplitude (microphone level) as a normalized number in the [0, 1] range.

Intended for driving live visualizations such as VU meters or waveforms while recording. Returns 0 when no recording is active. Designed for UI-rate polling. A 60 to 100 ms interval is a good starting point for a waveform. Avoid calling it in a tight loop; each call crosses the JS/native bridge.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const result = await CapacitorAudioRecorder.getCurrentAmplitude();
console.log(result);

Return the current permission state for accessing the microphone.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const result = await CapacitorAudioRecorder.checkPermissions();
console.log(result);

Request permission to access the microphone.

import { CapacitorAudioRecorder } from '@capgo/capacitor-audio-recorder';
const result = await CapacitorAudioRecorder.requestPermissions();
console.log(result);

Options accepted by .

export interface StartRecordingOptions {
/**
* The audio session category options for recording. Only available on iOS.
*
* @since 1.0.0
*/
audioSessionCategoryOptions?: AudioSessionCategoryOption[];
/**
* The audio session mode for recording. Only available on iOS.
*
* @since 1.0.0
*/
audioSessionMode?: AudioSessionMode;
/**
* The audio bit rate in bytes per second.
* Only available on Android and iOS.
*
* @since 1.0.0
*/
bitRate?: number;
/**
* The audio sample rate in Hz.
* Only available on Android and iOS.
*
* @since 1.0.0
*/
sampleRate?: number;
/**
* When `true`, reset the iOS `AVAudioSession` category to `.playback` when recording stops or is canceled.
*
* @since 8.2.10
*/
resetToPlaybackOnStop?: boolean;
/**
* The container the recording is written in. Only available on Android and iOS.
*
* @default 'm4a'
* @since 8.4.0
*/
format?: RecordingFormat;
}
export type RecordingFormat = 'm4a' | 'aac';

Result returned by .

export interface StopRecordingResult {
/**
* The recorded audio as a Blob. Only available on Web.
*
* @since 1.0.0
*/
blob?: Blob;
/**
* The duration of the recording in milliseconds.
*
* @since 1.0.0
*/
duration?: number;
/**
* The URI pointing to the recorded file. Only available on Android and iOS.
*
* @since 1.0.0
*/
uri?: string;
/**
* `true` when the segments of an interrupted recording could not be joined. Only available on iOS.
*
* @since 8.4.0
*/
mergeFailed?: boolean;
/**
* Segment URIs in recording order when `mergeFailed` is `true`. Only available on iOS.
*
* @since 8.4.0
*/
segments?: string[];
}

Result returned by .

export interface GetRecordingStatusResult {
/**
* The current recording status.
*
* @since 1.0.0
*/
status: RecordingStatus;
}

Result returned by .

export interface GetCurrentAmplitudeResult {
/**
* The current input amplitude normalized to the `[0, 1]` range, where `0`
* represents silence and `1` represents the maximum level the platform can
* report. The value is `0` when no recording is active.
*
* Note: the source signal differs between platforms. Android reports the
* peak sample amplitude since the last call, iOS reports the average power
* in dB converted to linear, and Web reports the RMS of the latest frame.
* Consumers that need cross-platform parity may want to apply a
* per-platform scaling curve.
*
* @since 8.1.0
*/
value: number;
}

Permission information returned by and .

export interface PermissionStatus {
/**
* The permission state for audio recording.
*
* @since 1.0.0
*/
recordAudio: PermissionState;
}

Event emitted when an error occurs during recording.

export interface RecordingErrorEvent {
/**
* The error message.
*
* @since 1.0.0
*/
message: string;
}

Event emitted when a recording completes.

export type RecordingStoppedEvent = StopRecordingResult;

Audio session category options available on iOS.

export enum AudioSessionCategoryOption {
AllowAirPlay = 'ALLOW_AIR_PLAY',
AllowBluetooth = 'ALLOW_BLUETOOTH',
AllowBluetoothA2DP = 'ALLOW_BLUETOOTH_A2DP',
DefaultToSpeaker = 'DEFAULT_TO_SPEAKER',
DuckOthers = 'DUCK_OTHERS',
InterruptSpokenAudioAndMixWithOthers = 'INTERRUPT_SPOKEN_AUDIO_AND_MIX_WITH_OTHERS',
MixWithOthers = 'MIX_WITH_OTHERS',
OverrideMutedMicrophoneInterruption = 'OVERRIDE_MUTED_MICROPHONE_INTERRUPTION',
}

Audio session modes available on iOS.

export enum AudioSessionMode {
Default = 'DEFAULT',
GameChat = 'GAME_CHAT',
Measurement = 'MEASUREMENT',
SpokenAudio = 'SPOKEN_AUDIO',
VideoChat = 'VIDEO_CHAT',
VideoRecording = 'VIDEO_RECORDING',
VoiceChat = 'VOICE_CHAT',
}

The recording status.

export enum RecordingStatus {
Inactive = 'INACTIVE',
Recording = 'RECORDING',
Paused = 'PAUSED',
}

Platform permission states supported by Capacitor.

export type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied';

This page is generated from the plugin’s src/definitions.ts. Re-run the sync when the public API changes upstream.

If you are using Getting Started to plan native media and interface behavior, connect it with Using @capgo/capacitor-audio-recorder for the native capability in Using @capgo/capacitor-audio-recorder, Using @capgo/capacitor-live-activities for the native capability in Using @capgo/capacitor-live-activities, @capgo/capacitor-live-activities for the implementation detail in @capgo/capacitor-live-activities, Using @capgo/capacitor-video-player for the native capability in Using @capgo/capacitor-video-player, and @capgo/capacitor-video-player for the implementation detail in @capgo/capacitor-video-player.