Listen & Identify
Upload a bird call recording or try our sample. Chirp identifies the species in seconds.
Analysing audio…
{error}
)}
Upload or select an audio clip
to identify the species
import React, { useState, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import Header from './components/Header' import AudioUploader from './components/AudioUploader' import Spectrogram from './components/Spectrogram' import BirdResult from './components/BirdResult' import RangeMap from './components/RangeMap' import { enrichResult } from './data/mockData' import { useSpectrogram } from './hooks/useSpectrogram' import styles from './App.module.css' const API_URL = 'https://mg643-chirp.hf.space' export default function App() { const [audioSelection, setAudioSelection] = useState(null) const [isLoading, setIsLoading] = useState(false) const [result, setResult] = useState(null) const [error, setError] = useState(null) const { spectrogramUrl, generateSpectrogram, isGenerating } = useSpectrogram() const handleAudioSelected = useCallback(async (selection) => { setAudioSelection(selection) setResult(null) setError(null) if (selection.file) { generateSpectrogram(selection.file) } else if (selection.sampleId) { try { const res = await fetch(`/samples/${selection.sampleId}.ogg`) const blob = await res.blob() generateSpectrogram(new File([blob], `${selection.sampleId}.ogg`, { type: 'audio/ogg' })) } catch (err) { console.warn('Spectrogram generation failed for sample:', err) } } }, [generateSpectrogram]) const handleIdentify = useCallback(async () => { if (!audioSelection) return setIsLoading(true) setError(null) try { let audioFile = audioSelection.file if (!audioFile && audioSelection.sampleId) { const res = await fetch(`/samples/${audioSelection.sampleId}.ogg`) const blob = await res.blob() audioFile = new File([blob], `${audioSelection.sampleId}.ogg`, { type: 'audio/ogg' }) } const formData = new FormData() formData.append('file', audioFile) const res = await fetch(`${API_URL}/predict?top_k=3`, { method: 'POST', body: formData, }) if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`) const data = await res.json() setResult(enrichResult(data)) } catch (err) { console.error('Identify failed:', err) setError('Could not reach the identification service. Please try again.') } finally { setIsLoading(false) } }, [audioSelection]) const hasAudio = audioSelection && (audioSelection.file || audioSelection.sampleId) return (
Listen & Identify
Upload a bird call recording or try our sample. Chirp identifies the species in seconds.
Analysing audio…
{error}
)}
Upload or select an audio clip
to identify the species