> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sammylabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Microphone Permissions

> Complete guide for managing microphone permissions in voice-enabled applications with sammy-three

# Microphone Permissions

> Comprehensive solution for managing microphone permissions in voice-enabled web applications

The `useMicrophonePermission` hook provides a robust foundation for handling microphone permissions in voice-enabled applications. This guide covers implementation patterns, best practices, and complete examples for seamless voice interaction.

## Core Concepts

### Permission States

The hook manages six distinct permission states that cover all possible scenarios:

<Cards>
  <Card title="idle" icon="circle">
    Initial state, no permission check performed yet
  </Card>

  <Card title="prompt" icon="question-circle">
    Browser will prompt user for permission
  </Card>

  <Card title="granted" icon="check-circle">
    User has granted microphone access
  </Card>

  <Card title="denied" icon="times-circle">
    User has explicitly denied microphone access
  </Card>

  <Card title="error" icon="exclamation-triangle">
    An error occurred (device not found, already in use, etc.)
  </Card>

  <Card title="unsupported" icon="browser">
    Browser doesn't support MediaDevices API
  </Card>
</Cards>

### Permission Flow Visualization

The permission flow follows a predictable pattern that ensures smooth user experience:

```mermaid theme={null}
graph TD
    A[Initial State: idle] --> B[Check Permission]
    B --> C{Permission Status}
    C -->|Prompt Required| D[Show UI Dialog]
    C -->|Already Granted| E[Start Agent]
    C -->|Denied| F[Show Instructions]
    D --> G[User Clicks Enable]
    G --> H[Browser Permission Prompt]
    H -->|User Grants| E
    H -->|User Denies| F
    F --> I[Manual Browser Settings]
    I --> J[Refresh & Check Again]
```

## Hook API Reference

### Basic Usage

Import and use the hook to manage microphone permissions in your components:

```typescript Basic Hook Usage theme={null}
import { useMicrophonePermission } from '@sammy-labs/sammy-three';

const MyComponent = () => {
  const {
    state,                      // Current permission state
    error,                      // Error message if any
    stream,                     // MediaStream when granted
    isPermissionGranted,        // Boolean: permission granted
    isPermissionDenied,         // Boolean: permission denied
    needsPermission,            // Boolean: needs user action
    checkPermission,            // Check current permission
    checkPermissionWithPolling, // Check with auto-retry
    request,                    // Request permission
    refresh,                    // Refresh permission state
    stop                        // Stop stream and cleanup
  } = useMicrophonePermission({
    onMicrophonePermissionRequired: () => {
      // Callback when permission is needed
      console.log('Microphone permission required');
    }
  });
};
```

### Return Values

<ResponseField name="state" type="PermissionStateType" required>
  Current permission state. One of: `idle`, `prompt`, `granted`, `denied`, `error`, `unsupported`
</ResponseField>

<ResponseField name="error" type="string | null">
  Error message when state is `error`. Null otherwise.
</ResponseField>

<ResponseField name="stream" type="MediaStream | null">
  Active media stream when permission is granted. Null otherwise.
</ResponseField>

<ResponseField name="isPermissionGranted" type="boolean">
  Convenience boolean that's true when state is `granted`.
</ResponseField>

<ResponseField name="isPermissionDenied" type="boolean">
  Convenience boolean that's true when state is `denied`.
</ResponseField>

<ResponseField name="needsPermission" type="boolean">
  Indicates whether user action is needed to grant permission.
</ResponseField>

<ResponseField name="checkPermission" type="() => Promise<PermissionCheckResult>">
  Checks current permission status without requesting access.
</ResponseField>

<ResponseField name="checkPermissionWithPolling" type="(callback?) => Promise<boolean>">
  Checks permission with automatic retry logic. Useful for waiting for user action.
</ResponseField>

<ResponseField name="request" type="() => Promise<void>">
  Requests microphone permission from the browser.
</ResponseField>

<ResponseField name="refresh" type="() => Promise<void>">
  Refreshes the current permission state.
</ResponseField>

<ResponseField name="stop" type="() => void">
  Stops the media stream and performs cleanup.
</ResponseField>

## Implementation Patterns

<Tabs>
  <Tab title="Modal-Based Flow" icon="window">
    ### Pattern 1: Modal-Based Permission Flow (Recommended)

    This is the recommended pattern for guide/walkthrough scenarios where you want full control over the user experience.

    <CodeGroup>
      ```typescript MicrophonePermissionModal.tsx theme={null}
      import React, { useEffect, useState } from 'react';
      import { 
        useMicrophonePermission, 
        useSammyAgentContext, 
        AgentMode 
      } from '@sammy-labs/sammy-three';

      export const MicrophonePermissionModal = ({ open, onOpenChange }) => {
        const [isRequesting, setIsRequesting] = useState(false);
        const { startAgent, guides } = useSammyAgentContext();
        
        const {
          state,
          error,
          isPermissionGranted,
          isPermissionDenied,
          checkPermission,
          request,
          refresh
        } = useMicrophonePermission();

        // Initial permission check
        useEffect(() => {
          if (open) {
            checkPermission();
          }
        }, [open, checkPermission]);

        // Auto-start agent when permission is granted
        useEffect(() => {
          if (isPermissionGranted && open) {
            handleStartAgent();
          }
        }, [isPermissionGranted, open]);

        const handleStartAgent = async () => {
          try {
            await startAgent({
              agentMode: AgentMode.USER,
              guideId: guides?.currentGuide?.guideId,
            });
            onOpenChange(false); // Close modal after successful start
          } catch (error) {
            console.error('Failed to start agent:', error);
          }
        };

        const handleRequestPermission = async () => {
          setIsRequesting(true);
          try {
            await request();
            // If successful, the useEffect above will handle starting the agent
          } finally {
            setIsRequesting(false);
          }
        };

        if (!open) return null;

        return (
          <div className="modal-overlay">
            <div className="modal-content">
              {renderContent()}
            </div>
          </div>
        );

        function renderContent() {
          // Permission already granted - auto-starting
          if (isPermissionGranted) {
            return (
              <div className="permission-granted">
                <h3>Starting Voice Assistant...</h3>
                <div className="spinner" />
              </div>
            );
          }

          // Permission denied - show instructions
          if (isPermissionDenied) {
            return (
              <div className="permission-denied">
                <h3>🚫 Microphone Access Blocked</h3>
                <p>To use voice features, you need to grant microphone access:</p>
                <ol>
                  <li>Click the lock icon in your browser's address bar</li>
                  <li>Find "Microphone" settings</li>
                  <li>Change from "Block" to "Allow"</li>
                  <li>Click "Check Again" below</li>
                </ol>
                <button onClick={refresh}>🔄 Check Again</button>
              </div>
            );
          }

          // Error state
          if (state === 'error' && error) {
            return (
              <div className="permission-error">
                <h3>⚠️ Microphone Error</h3>
                <p>{error}</p>
                <button onClick={refresh}>Try Again</button>
              </div>
            );
          }

          // Default: Need to request permission
          return (
            <div className="permission-prompt">
              <h3>🎤 Enable Voice Assistant?</h3>
              <p>Our AI assistant will guide you through the platform using voice.</p>
              <p>Click "Enable Microphone" and allow access when prompted.</p>
              <button 
                onClick={handleRequestPermission}
                disabled={isRequesting}
              >
                {isRequesting ? 'Requesting...' : '🎤 Enable Microphone'}
              </button>
            </div>
          );
        }
      };
      ```

      ```css modal-styles.css theme={null}
      /* Modal Styles */
      .modal-overlay {
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.5);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 9999;
      }

      .modal-content {
        background: white;
        border-radius: 12px;
        width: 90%;
        max-width: 500px;
        max-height: 80vh;
        overflow: auto;
        box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
        padding: 20px;
      }

      /* Permission States */
      .permission-granted {
        text-align: center;
        padding: 40px;
      }

      .permission-denied ol {
        text-align: left;
        margin: 20px 0;
        padding-left: 20px;
      }

      .spinner {
        width: 40px;
        height: 40px;
        border: 4px solid #e5e7eb;
        border-top-color: #3b82f6;
        border-radius: 50%;
        animation: spin 1s linear infinite;
        margin: 0 auto 16px;
      }

      @keyframes spin {
        to { transform: rotate(360deg); }
      }
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Auto Polling" icon="sync">
    ### Pattern 2: Automatic Permission with Polling

    For scenarios where you want to automatically handle permission checks with retry logic:

    ```typescript AutoPermissionHandler.tsx theme={null}
    export const AutoPermissionHandler = () => {
      const { startAgent } = useSammyAgentContext();
      const { checkPermissionWithPolling } = useMicrophonePermission();
      const [showPermissionUI, setShowPermissionUI] = useState(false);

      const initializeAgent = async () => {
        // This will automatically poll for permission changes
        const hasPermission = await checkPermissionWithPolling(() => {
          // Called when permission is needed
          setShowPermissionUI(true);
        });

        if (hasPermission) {
          await startAgent({ agentMode: AgentMode.USER });
          setShowPermissionUI(false);
        } else {
          console.log('Permission denied or timeout');
        }
      };

      useEffect(() => {
        initializeAgent();
      }, []);

      if (showPermissionUI) {
        return <PermissionInstructions />;
      }

      return null;
    };
    ```
  </Tab>

  <Tab title="Inline Check" icon="cursor-click">
    ### Pattern 3: Inline Permission Check

    For simpler use cases where you want to check permission inline:

    ```typescript VoiceButton.tsx theme={null}
    export const VoiceButton = () => {
      const { checkPermission, request, isPermissionGranted } = useMicrophonePermission();
      const [isChecking, setIsChecking] = useState(false);

      const handleClick = async () => {
        setIsChecking(true);
        
        const result = await checkPermission();
        
        if (result.needsPermission) {
          await request();
        }
        
        if (isPermissionGranted) {
          // Start voice interaction
          console.log('Starting voice interaction...');
        }
        
        setIsChecking(false);
      };

      return (
        <button onClick={handleClick} disabled={isChecking}>
          {isChecking ? 'Checking...' : '🎤 Start Voice'}
        </button>
      );
    };
    ```
  </Tab>
</Tabs>

## UI/UX Best Practices

### Clear Permission Context

<Card title="Explain the Why" icon="info-circle">
  Always explain WHY you need microphone access before requesting. Users are more likely to grant permissions when they understand the value.

  ```jsx Permission Context Example theme={null}
  <div className="permission-context">
    <h3>Voice-Guided Experience</h3>
    <ul>
      <li>✓ Real-time voice guidance through the platform</li>
      <li>✓ Answer your questions as you navigate</li>
      <li>✓ Hands-free interaction</li>
    </ul>
    <p>We only access your microphone during active sessions.</p>
  </div>
  ```
</Card>

### Progressive Disclosure

<Card title="Wait for User Intent" icon="hand-pointer">
  Don't request permissions immediately on page load. Wait for explicit user action to avoid feeling intrusive.

  ```jsx Progressive Disclosure Example theme={null}
  export const GuidedTour = () => {
    const [showPermissionModal, setShowPermissionModal] = useState(false);
    
    return (
      <>
        <button onClick={() => setShowPermissionModal(true)}>
          Start Guided Tour
        </button>
        
        {showPermissionModal && (
          <MicrophonePermissionModal 
            open={showPermissionModal}
            onOpenChange={setShowPermissionModal}
          />
        )}
      </>
    );
  };
  ```
</Card>

### State-Specific Messaging

Provide clear, actionable messages for each permission state:

```typescript State Message Handler theme={null}
const getStateMessage = (state: PermissionStateType, error?: string) => {
  switch (state) {
    case 'prompt':
      return {
        title: 'Enable Microphone Access',
        message: 'Click below to enable your microphone for voice guidance.',
        action: 'Enable Microphone'
      };
    
    case 'denied':
      return {
        title: 'Microphone Access Blocked',
        message: 'You\'ll need to update your browser settings to continue.',
        action: 'View Instructions'
      };
    
    case 'error':
      return {
        title: 'Microphone Error',
        message: error || 'Unable to access microphone',
        action: 'Try Again'
      };
    
    case 'granted':
      return {
        title: 'Ready to Start',
        message: 'Microphone access granted!',
        action: null
      };
    
    default:
      return {
        title: 'Checking Permissions',
        message: 'Please wait...',
        action: null
      };
  }
};
```

## Error Handling

### Common Error Scenarios

Handle different error types with specific solutions:

```typescript Error Handler theme={null}
const handleMicrophoneError = (error: string) => {
  const errorHandlers = {
    'No microphone device found.': {
      icon: '🎤',
      solution: 'Please connect a microphone to your device.',
      canRetry: true
    },
    'Microphone is already in use by another application.': {
      icon: '⚠️',
      solution: 'Close other apps using your microphone (Zoom, Teams, etc.)',
      canRetry: true
    },
    'Microphone permission was denied.': {
      icon: '🚫',
      solution: 'Update browser settings to allow microphone access.',
      canRetry: false,
      showInstructions: true
    }
  };

  const handler = errorHandlers[error] || {
    icon: '❌',
    solution: 'Please try again or contact support.',
    canRetry: true
  };

  return handler;
};
```

### Graceful Degradation

Always provide fallback options when voice isn't available:

```typescript Adaptive Guide Component theme={null}
export const AdaptiveGuide = () => {
  const { state } = useMicrophonePermission();
  const isVoiceAvailable = state === 'granted';

  return (
    <div className="guide-container">
      {isVoiceAvailable ? (
        <VoiceGuidedExperience />
      ) : (
        <TextBasedGuide 
          showVoicePrompt={state === 'prompt'}
          onEnableVoice={() => /* handle permission request */}
        />
      )}
    </div>
  );
};
```

## Complete Integration Example

Here's a comprehensive implementation that combines all the best practices:

<CodeGroup>
  ```typescript SammyStartWalkthroughModal.tsx expandable theme={null}
  /**
   * Sammy Start Walkthrough Modal
   * Modal that automatically appears when a guide is available
   * Handles microphone permission flow seamlessly
   */
  import React, { useEffect, useState, useCallback } from 'react';
  import { 
    useMicrophonePermission, 
    useSammyAgentContext,
    AgentMode 
  } from '@sammy-labs/sammy-three';

  export const SammyStartWalkthroughModal = () => {
    const [isModalOpen, setIsModalOpen] = useState(false);
    const [hasStartedWalkthrough, setHasStartedWalkthrough] = useState(false);
    const [isStarting, setIsStarting] = useState(false);
    
    const { activeSession, startAgent, guides } = useSammyAgentContext();
    const {
      state,
      error,
      isPermissionGranted,
      isPermissionDenied,
      needsPermission,
      checkPermission,
      request,
      refresh
    } = useMicrophonePermission();

    const startWalkthroughSession = useCallback(async () => {
      try {
        setHasStartedWalkthrough(true);
        await startAgent({
          agentMode: AgentMode.USER,
          guideId: guides?.currentGuide?.guideId,
        });
        setIsModalOpen(false);
        setIsStarting(false);
      } catch (error) {
        console.error('Error starting walkthrough:', error);
        setIsStarting(false);
        // Handle error appropriately
      }
    }, [startAgent, guides?.currentGuide?.guideId]);

    // Open modal immediately when guide is available but session isn't active
    useEffect(() => {
      if (guides?.currentGuide && !activeSession && !hasStartedWalkthrough) {
        setIsModalOpen(true);
        checkPermission(); // Pre-check permission
      }
    }, [guides?.currentGuide, activeSession, hasStartedWalkthrough, checkPermission]);

    // Auto-start when permission is granted while modal is open and starting
    useEffect(() => {
      if (isPermissionGranted && isModalOpen && isStarting) {
        startWalkthroughSession();
      }
    }, [isPermissionGranted, isModalOpen, isStarting, startWalkthroughSession]);

    const handleStartWalkthrough = async () => {
      setIsStarting(true);
      
      try {
        // Step 1: Check current permission state
        const permissionResult = await checkPermission();
        
        // Step 2: If permission already granted, start immediately
        if (permissionResult.state === 'granted' || isPermissionGranted) {
          await startWalkthroughSession();
          return;
        }
        
        // Step 3: Request permission if needed
        if (permissionResult.needsPermission) {
          await request();
          // The useEffect above will handle starting when permission is granted
        }
      } catch (error) {
        console.error('Error in handleStartWalkthrough:', error);
        setIsStarting(false);
      }
    };

    const handleRefresh = async () => {
      await refresh();
      // Re-check after refresh
      const result = await checkPermission();
      if (result.state === 'granted') {
        await startWalkthroughSession();
      }
    };

    const handleClose = () => {
      setIsModalOpen(false);
      setIsStarting(false);
    };

    if (!isModalOpen || !guides?.currentGuide) return null;

    const renderModalContent = () => {
      // Starting the walkthrough (permission already granted or being processed)
      if (isStarting && (isPermissionGranted || state === 'granted')) {
        return (
          <div className="sammy-modal-auto-starting">
            <div className="sammy-modal-spinner" />
            <h3>Starting Voice Assistant...</h3>
            <p>Preparing your guided experience</p>
          </div>
        );
      }

      // Permission denied - show instructions
      if (isPermissionDenied) {
        return (
          <div className="sammy-modal-permission-denied">
            <div className="sammy-modal-icon">🚫</div>
            <h3>Microphone Access Blocked</h3>
            <p>To use the voice-guided walkthrough, you need to grant microphone access:</p>
            <ol className="sammy-modal-instructions">
              <li>Click the lock/info icon in your browser's address bar</li>
              <li>Find "Microphone" in the site settings</li>
              <li>Change from "Block" to "Allow"</li>
              <li>Click "Check Again" below</li>
            </ol>
            <div className="sammy-modal-button-group">
              <button 
                className="sammy-modal-btn sammy-modal-btn-secondary"
                onClick={handleClose}
              >
                Cancel
              </button>
              <button 
                className="sammy-modal-btn sammy-modal-btn-primary"
                onClick={handleRefresh}
              >
                🔄 Check Again
              </button>
            </div>
          </div>
        );
      }

      // Error state
      if (state === 'error' && error) {
        return (
          <div className="sammy-modal-error">
            <div className="sammy-modal-icon">⚠️</div>
            <h3>Microphone Error</h3>
            <p className="sammy-modal-error-message">{error}</p>
            <div className="sammy-modal-button-group">
              <button 
                className="sammy-modal-btn sammy-modal-btn-secondary"
                onClick={handleClose}
              >
                Cancel
              </button>
              <button 
                className="sammy-modal-btn sammy-modal-btn-primary"
                onClick={handleRefresh}
              >
                Try Again
              </button>
            </div>
          </div>
        );
      }

      // Browser not supported
      if (state === 'unsupported') {
        return (
          <div className="sammy-modal-unsupported">
            <div className="sammy-modal-icon">⚠️</div>
            <h3>Browser Not Supported</h3>
            <p>Your browser doesn't support microphone access. Please use Chrome, Firefox, or Safari.</p>
            <div className="sammy-modal-button-group">
              <button 
                className="sammy-modal-btn sammy-modal-btn-primary"
                onClick={handleClose}
              >
                OK
              </button>
            </div>
          </div>
        );
      }

      // Default: Initial prompt to start walkthrough
      return (
        <div className="sammy-modal-initial">
          <div className="sammy-modal-icon">
            <div className="sammy-modal-speech-icon">💬</div>
          </div>
          
          <h2>Start AI Walkthrough Now?</h2>
          
          <p className="sammy-modal-description">
            Our AI assistant will walk you through the platform in real time, 
            answering your questions and guiding you step by step based on what's 
            on your screen.
          </p>

          <div className="sammy-modal-features">
            <div className="sammy-modal-feature">
              <span className="sammy-modal-feature-icon">🎯</span>
              <span>Real-time guidance through the platform</span>
            </div>
            <div className="sammy-modal-feature">
              <span className="sammy-modal-feature-icon">💬</span>
              <span>Interactive Q&A as you navigate</span>
            </div>
            <div className="sammy-modal-feature">
              <span className="sammy-modal-feature-icon">🎤</span>
              <span>Natural voice interaction</span>
            </div>
          </div>

          {needsPermission && (
            <p className="sammy-modal-permission-note">
              <span className="sammy-modal-info-icon">ℹ️</span>
              Clicking "Start Now" will request microphone access
            </p>
          )}

          <div className="sammy-modal-button-group">
            <button 
              className="sammy-modal-btn sammy-modal-btn-secondary"
              onClick={handleClose}
            >
              Not Now
            </button>
            <button 
              className="sammy-modal-btn sammy-modal-btn-primary"
              onClick={handleStartWalkthrough}
              disabled={isStarting}
            >
              {isStarting ? 'Starting...' : 'Start Now'}
            </button>
          </div>
        </div>
      );
    };

    return (
      <div className="sammy-modal-backdrop" onClick={handleClose}>
        <div className="sammy-modal" onClick={(e) => e.stopPropagation()}>
          <button 
            className="sammy-modal-close"
            onClick={handleClose}
            aria-label="Close modal"
          >
            ×
          </button>
          
          <div className="sammy-modal-content">
            {renderModalContent()}
          </div>
        </div>
      </div>
    );
  };
  ```

  ```css styles.css expandable theme={null}
  /* Modal Styles */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
  }

  .modal {
    background: white;
    border-radius: 12px;
    width: 90%;
    max-width: 500px;
    max-height: 80vh;
    overflow: auto;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
  }

  .modal-header {
    padding: 20px;
    border-bottom: 1px solid #e5e7eb;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .modal-body {
    padding: 20px;
  }

  /* Permission States */
  .request-permission .feature-list {
    margin-bottom: 20px;
  }

  .request-permission ul {
    list-style: none;
    padding: 0;
    margin: 10px 0;
  }

  .request-permission li {
    padding: 8px 0;
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .privacy-note {
    font-size: 14px;
    color: #6b7280;
    margin: 16px 0;
  }

  /* Button Group */
  .button-group {
    display: flex;
    gap: 12px;
    margin-top: 20px;
  }

  .btn-primary {
    flex: 1;
    padding: 12px 20px;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 8px;
    font-weight: 500;
    cursor: pointer;
  }

  .btn-primary:hover {
    background: #2563eb;
  }

  .btn-secondary {
    flex: 1;
    padding: 12px 20px;
    background: transparent;
    color: #6b7280;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    font-weight: 500;
    cursor: pointer;
  }

  /* Permission Denied */
  .permission-denied {
    text-align: center;
  }

  .permission-denied ol {
    text-align: left;
    margin: 20px 0;
    padding-left: 20px;
  }

  .permission-denied li {
    margin: 8px 0;
  }

  /* Loading State */
  .auto-starting {
    text-align: center;
    padding: 40px;
  }

  .spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #e5e7eb;
    border-top-color: #3b82f6;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    margin: 0 auto 16px;
  }

  @keyframes spin {
    to { transform: rotate(360deg); }
  }

  /* Error State */
  .error-state {
    text-align: center;
    padding: 20px;
  }

  .error-state h3 {
    color: #ef4444;
    margin-bottom: 12px;
  }
  ```
</CodeGroup>

## Common Pitfalls and Mistakes

<Warning>
  These are critical mistakes that will cause your microphone permission flow to fail. Review this section carefully to avoid common implementation errors.
</Warning>

### ❌ Incorrect Implementation (Will Fail)

```typescript Broken Implementation - DO NOT USE theme={null}
const handleStartWalkthrough = async () => {
  try {
    // ❌ WRONG: Directly starting agent without permission check
    handleCloseModal();
    setHasStartedWalkthrough(true);
    await startAgent({
      agentMode: AgentMode.USER,
      guideId: guides?.currentGuide?.guideId,
    });
  } catch (error) {
    console.error('Error starting walkthrough:', error);
  }
};
```

### ✅ Correct Implementation

```typescript Proper Permission Flow theme={null}
const handleStartWalkthrough = async () => {
  setIsStarting(true);
  
  try {
    // ✅ Step 1: ALWAYS check current permission state first
    const permissionResult = await checkPermission();
    
    // ✅ Step 2: If permission already granted, start immediately
    if (permissionResult.state === 'granted' || isPermissionGranted) {
      await startWalkthroughSession();
      return;
    }
    
    // ✅ Step 3: Request permission if needed
    if (permissionResult.needsPermission) {
      await request();
      // The useEffect will handle starting when permission is granted
    }
  } catch (error) {
    console.error('Error in handleStartWalkthrough:', error);
    setIsStarting(false);
  }
};
```

### Critical Requirements Checklist

<Tabs>
  <Tab title="Missing Hook Usage" icon="exclamation-triangle">
    ### Problem

    **Not using the `useMicrophonePermission` hook at all**

    ```typescript Common Mistake theme={null}
    // ❌ WRONG: No permission handling
    const MyComponent = () => {
      const { startAgent } = useSammyAgentContext();
      
      const handleStart = async () => {
        await startAgent({ agentMode: AgentMode.USER }); // Will fail!
      };
    };
    ```

    ### Solution

    **Always import and use the permission hook**

    ```typescript Correct Approach theme={null}
    // ✅ CORRECT: Using permission hook
    const MyComponent = () => {
      const { startAgent } = useSammyAgentContext();
      const {
        state,
        error,
        isPermissionGranted,
        isPermissionDenied,
        needsPermission,
        checkPermission,
        request,
        refresh
      } = useMicrophonePermission(); // Essential!
      
      const handleStart = async () => {
        const result = await checkPermission();
        if (result.needsPermission) {
          await request();
        }
        if (isPermissionGranted) {
          await startAgent({ agentMode: AgentMode.USER });
        }
      };
    };
    ```
  </Tab>

  <Tab title="No Permission Check" icon="times-circle">
    ### Problem

    **Starting agent without checking permissions first**

    ```typescript Common Mistake theme={null}
    // ❌ WRONG: No permission check
    const handleClick = async () => {
      await startAgent({ // Will fail silently!
        agentMode: AgentMode.USER,
        guideId: guideId
      });
    };
    ```

    ### Solution

    **Always check permission before starting agent**

    ```typescript Correct Approach theme={null}
    // ✅ CORRECT: Check permission first
    const handleClick = async () => {
      const permissionResult = await checkPermission();
      
      if (permissionResult.state === 'granted') {
        await startAgent({
          agentMode: AgentMode.USER,
          guideId: guideId
        });
      } else if (permissionResult.needsPermission) {
        await request();
        // Handle the flow after permission is granted
      }
    };
    ```
  </Tab>

  <Tab title="Missing State Tracking" icon="sync-alt">
    ### Problem

    **Not tracking the starting/loading state**

    ```typescript Common Mistake theme={null}
    // ❌ WRONG: No state tracking
    const MyComponent = () => {
      const handleStart = async () => {
        await checkPermission();
        await request();
        await startAgent(); // User sees no feedback during this process
      };
      
      return <button onClick={handleStart}>Start</button>;
    };
    ```

    ### Solution

    **Track state for better UX**

    ```typescript Correct Approach theme={null}
    // ✅ CORRECT: Track state for user feedback
    const MyComponent = () => {
      const [isStarting, setIsStarting] = useState(false);
      
      const handleStart = async () => {
        setIsStarting(true);
        try {
          const result = await checkPermission();
          if (result.needsPermission) {
            await request();
          }
          if (isPermissionGranted) {
            await startAgent();
          }
        } finally {
          setIsStarting(false);
        }
      };
      
      return (
        <button onClick={handleStart} disabled={isStarting}>
          {isStarting ? 'Starting...' : 'Start'}
        </button>
      );
    };
    ```
  </Tab>

  <Tab title="No Auto-Start Logic" icon="play-circle">
    ### Problem

    **Not handling auto-start when permission is granted**

    ```typescript Common Mistake theme={null}
    // ❌ WRONG: User has to click again after granting permission
    const handleStart = async () => {
      if (needsPermission) {
        await request();
        // Nothing happens after permission is granted!
      }
    };
    ```

    ### Solution

    **Use useEffect for auto-start**

    ```typescript Correct Approach theme={null}
    // ✅ CORRECT: Auto-start when permission is granted
    const [isStarting, setIsStarting] = useState(false);

    // Auto-start effect
    useEffect(() => {
      if (isPermissionGranted && isModalOpen && isStarting) {
        startWalkthroughSession();
      }
    }, [isPermissionGranted, isModalOpen, isStarting]);

    const handleStart = async () => {
      setIsStarting(true);
      
      const result = await checkPermission();
      if (result.state === 'granted') {
        await startWalkthroughSession();
      } else if (result.needsPermission) {
        await request();
        // useEffect will handle starting when granted
      }
    };
    ```
  </Tab>

  <Tab title="Missing Error States" icon="exclamation">
    ### Problem

    **Not handling all permission states in UI**

    ```typescript Common Mistake theme={null}
    // ❌ WRONG: Only handling happy path
    return (
      <div>
        {isPermissionGranted ? (
          <div>Starting...</div>
        ) : (
          <button onClick={request}>Enable Microphone</button>
        )}
      </div>
    );
    ```

    ### Solution

    **Handle all states explicitly**

    ```typescript Correct Approach theme={null}
    // ✅ CORRECT: Handle all states
    const renderContent = () => {
      if (isPermissionGranted) {
        return <div>Starting voice assistant...</div>;
      }
      
      if (isPermissionDenied) {
        return (
          <div>
            <h3>Microphone Blocked</h3>
            <p>Please update browser settings</p>
            <button onClick={refresh}>Check Again</button>
          </div>
        );
      }
      
      if (state === 'error') {
        return (
          <div>
            <h3>Error: {error}</h3>
            <button onClick={refresh}>Try Again</button>
          </div>
        );
      }
      
      if (state === 'unsupported') {
        return <div>Browser not supported</div>;
      }
      
      return (
        <button onClick={handleStartWalkthrough}>
          Enable Microphone
        </button>
      );
    };
    ```
  </Tab>
</Tabs>

### Key Implementation Points

<Card title="Essential Implementation Pattern" icon="code" color="#10b981">
  The correct pattern **ALWAYS** includes these elements:

  1. **Import and use `useMicrophonePermission` hook**
  2. **Check permission state before starting agent**
  3. **Handle permission request if needed**
  4. **Track loading/starting state**
  5. **Implement auto-start logic with useEffect**
  6. **Handle all permission states in UI**
  7. **Provide recovery options for denied/error states**
</Card>

## Troubleshooting

### Common Issues

<Steps>
  <Step title="Permission State Not Updating">
    Use the `refresh()` method to force a re-check of the permission state:

    ```typescript theme={null}
    const handlePermissionChange = async () => {
      await refresh();
      const newState = await checkPermission();
      console.log('Updated state:', newState);
    };
    ```
  </Step>

  <Step title="Browser Compatibility Issues">
    Check for browser support before attempting to use microphone features:

    ```typescript theme={null}
    const checkBrowserSupport = () => {
      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
        return {
          supported: false,
          message: 'Your browser doesn\'t support microphone access. Please use Chrome, Firefox, or Safari.'
        };
      }
      
      // Check for HTTPS (required for getUserMedia)
      if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
        return {
          supported: false,
          message: 'Microphone access requires HTTPS. Please use a secure connection.'
        };
      }
      
      return { supported: true };
    };
    ```
  </Step>

  <Step title="Handling Permission Reset">
    Re-check permissions when the browser tab becomes visible:

    ```typescript theme={null}
    useEffect(() => {
      const handleVisibilityChange = () => {
        if (document.visibilityState === 'visible') {
          // Re-check permissions when tab becomes visible
          checkPermission();
        }
      };

      document.addEventListener('visibilitychange', handleVisibilityChange);
      return () => {
        document.removeEventListener('visibilitychange', handleVisibilityChange);
      };
    }, [checkPermission]);
    ```
  </Step>

  <Step title="Stream Cleanup">
    Always ensure proper cleanup of media streams:

    ```typescript theme={null}
    useEffect(() => {
      return () => {
        // Hook handles this automatically, but for custom implementations:
        if (stream) {
          stream.getTracks().forEach(track => track.stop());
        }
      };
    }, [stream]);
    ```
  </Step>
</Steps>

## Best Practices

<Card title="Best Practices Checklist" icon="check-square" horizontal>
  Follow these guidelines for optimal microphone permission handling:
</Card>

1. **Always explain why** you need microphone access before requesting
2. **Check permission state** before attempting to request
3. **Handle all states** explicitly with appropriate UI
4. **Provide clear instructions** for permission recovery
5. **Test across browsers** and handle compatibility issues
6. **Clean up resources** properly when component unmounts
7. **Use polling sparingly** - only when expecting user action
8. **Provide fallbacks** for when voice isn't available
9. **Log errors** for debugging but show user-friendly messages
10. **Respect user choice** - don't repeatedly prompt if denied

<Note>
  Remember that microphone permission is a sensitive user action - always be transparent about why you need it and what you'll use it for. The modal-based pattern with clear messaging and progressive disclosure provides the best user experience for most use cases.
</Note>

## Next Steps

<Columns cols={2}>
  <Card title="Voice Activity Detection" icon="waveform" href="/features/vad">
    Learn about VAD configuration for optimal voice detection
  </Card>

  <Card title="Audio Processing" icon="sliders" href="/features/audio-processing">
    Explore audio processing features and optimization
  </Card>

  <Card title="Error Handling" icon="shield-exclamation" href="/features/error-handling">
    Comprehensive error handling strategies
  </Card>

  <Card title="Guides System" icon="route" href="/features/guides">
    Implement voice-guided experiences
  </Card>
</Columns>
