├── .eslintrc.json
├── .gitignore
├── README.md
├── app
├── favicon.ico
├── globals.css
├── layout.tsx
├── page.tsx
└── views
│ └── HomeView.tsx
├── components.json
├── lib
└── utils.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── next.svg
└── vercel.svg
├── tailwind.config.js
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Voice-to-Text Next.js App
3 |
4 | This application allows users to record their voice and receive a real-time transcript displayed on the screen. It's built using Next.js and utilizes Web Speech API for recording and speech recognition.
5 |
6 | 
7 |
8 | ## Table of Contents
9 |
10 | - [Demo](#demo)
11 | - [Features](#features)
12 | - [Getting Started](#getting-started)
13 | - [Prerequisites](#prerequisites)
14 | - [Installation](#installation)
15 | - [Usage](#usage)
16 | - [Built with](#built-with)
17 | - [Contributing](#contributing)
18 | - [License](#license)
19 |
20 | ## Demo
21 |
22 | 
23 |
24 | ## Features
25 |
26 | - Record your voice using the built-in microphone.
27 | - See real-time transcription of your speech on the screen.
28 | - Minimalistic user interface for easy interaction.
29 | - Built with responsive design in mind, works well on different devices.
30 |
31 | ## Getting Started
32 |
33 | Follow these instructions to get the project up and running on your local machine.
34 |
35 | ### Prerequisites
36 |
37 | - Node.js: Make sure you have Node.js installed. You can download it from [nodejs.org](https://nodejs.org/).
38 |
39 | ### Installation
40 |
41 | 1. Clone the repository:
42 |
43 | ```bash
44 | git clone https://github.com/your-username/voice-to-text-nextjs.git
45 | ```
46 |
47 | 2. Navigate to the project directory:
48 |
49 | ```bash
50 | cd voice-to-text-nextjs
51 | ```
52 |
53 | 3. Install the dependencies:
54 |
55 | ```bash
56 | npm install
57 | ```
58 |
59 | ### Usage
60 |
61 | 1. Start the development server:
62 |
63 | ```bash
64 | npm run dev
65 | ```
66 |
67 | 2. Open your browser and navigate to `http://localhost:3000`.
68 |
69 | 3. Allow microphone access when prompted.
70 |
71 | 4. Click the "Start Recording" button to begin recording your voice.
72 |
73 | 5. As you speak, you should see the transcription displayed on the screen in real-time.
74 |
75 | 6. Click the "Stop Recording" button to stop recording.
76 |
77 | ## Built with
78 |
79 | - [Next.js](https://nextjs.org/) - The React framework for building the app.
80 | - [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API) - Used for recording and speech recognition.
81 | - [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS framework for styling.
82 |
83 | ## Contributing
84 |
85 | Contributions are welcome! If you find any issues or want to enhance the app, feel free to submit a pull request.
86 |
87 | 1. Fork the repository.
88 | 2. Create a new branch.
89 | 3. Make your changes and commit them.
90 | 4. Push the changes to your fork.
91 | 5. Submit a pull request describing your changes.
92 |
93 | ## License
94 |
95 | This project is licensed under the [MIT License](LICENSE).
96 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sambowenhughes/voice-recording-with-nextjs/55080d9e9f0abd2a76f9df830364cf8d56e478be/app/favicon.ico
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import './globals.css'
2 | import type { Metadata } from 'next'
3 | import { Inter } from 'next/font/google'
4 |
5 | const inter = Inter({ subsets: ['latin'] })
6 |
7 | export const metadata: Metadata = {
8 | title: 'Create Next App',
9 | description: 'Generated by create next app',
10 | }
11 |
12 | export default function RootLayout({
13 | children,
14 | }: {
15 | children: React.ReactNode
16 | }) {
17 | return (
18 |
19 |
{children}
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import HomeView from "./views/HomeView";
2 |
3 | export default function Home() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/app/views/HomeView.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | // Import necessary modules and components
4 | import { useEffect, useState, useRef } from "react";
5 |
6 | // Declare a global interface to add the webkitSpeechRecognition property to the Window object
7 | declare global {
8 | interface Window {
9 | webkitSpeechRecognition: any;
10 | }
11 | }
12 |
13 | // Export the MicrophoneComponent function component
14 | export default function MicrophoneComponent() {
15 | // State variables to manage recording status, completion, and transcript
16 | const [isRecording, setIsRecording] = useState(false);
17 | const [recordingComplete, setRecordingComplete] = useState(false);
18 | const [transcript, setTranscript] = useState("");
19 |
20 | // Reference to store the SpeechRecognition instance
21 | const recognitionRef = useRef(null);
22 |
23 | // Function to start recording
24 | const startRecording = () => {
25 | setIsRecording(true);
26 | // Create a new SpeechRecognition instance and configure it
27 | recognitionRef.current = new window.webkitSpeechRecognition();
28 | recognitionRef.current.continuous = true;
29 | recognitionRef.current.interimResults = true;
30 |
31 | // Event handler for speech recognition results
32 | recognitionRef.current.onresult = (event: any) => {
33 | const { transcript } = event.results[event.results.length - 1][0];
34 |
35 | // Log the recognition results and update the transcript state
36 | console.log(event.results);
37 | setTranscript(transcript);
38 | };
39 |
40 | // Start the speech recognition
41 | recognitionRef.current.start();
42 | };
43 |
44 | // Cleanup effect when the component unmounts
45 | useEffect(() => {
46 | return () => {
47 | // Stop the speech recognition if it's active
48 | if (recognitionRef.current) {
49 | recognitionRef.current.stop();
50 | }
51 | };
52 | }, []);
53 |
54 | // Function to stop recording
55 | const stopRecording = () => {
56 | if (recognitionRef.current) {
57 | // Stop the speech recognition and mark recording as complete
58 | recognitionRef.current.stop();
59 | setRecordingComplete(true);
60 | }
61 | };
62 |
63 | // Toggle recording state and manage recording actions
64 | const handleToggleRecording = () => {
65 | setIsRecording(!isRecording);
66 | if (!isRecording) {
67 | startRecording();
68 | } else {
69 | stopRecording();
70 | }
71 | };
72 |
73 | // Render the microphone component with appropriate UI based on recording state
74 | return (
75 |
76 |
77 | {(isRecording || transcript) && (
78 |
79 |
80 |
81 |
82 | {recordingComplete ? "Recorded" : "Recording"}
83 |
84 |
85 | {recordingComplete
86 | ? "Thanks for talking."
87 | : "Start speaking..."}
88 |
89 |
90 | {isRecording && (
91 |
92 | )}
93 |
94 |
95 | {transcript && (
96 |
99 | )}
100 |
101 | )}
102 |
103 |
104 | {isRecording ? (
105 | // Button for stopping recording
106 |
118 | ) : (
119 | // Button for starting recording
120 |
135 | )}
136 |
137 |
138 |
139 | );
140 | }
141 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "slate",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {}
3 |
4 | module.exports = nextConfig
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "voice-to-text",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@types/node": "20.4.1",
13 | "@types/react": "18.2.14",
14 | "@types/react-dom": "18.2.6",
15 | "autoprefixer": "10.4.14",
16 | "eslint": "8.44.0",
17 | "eslint-config-next": "13.4.9",
18 | "mic-recorder-to-mp3": "^2.2.2",
19 | "next": "13.4.9",
20 | "postcss": "8.4.25",
21 | "react": "18.2.0",
22 | "react-dom": "18.2.0",
23 | "tailwindcss": "3.3.2",
24 | "tailwindcss-animate": "^1.0.6",
25 | "typescript": "5.1.6",
26 | "wavesurfer.js": "^7.1.4"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | './pages/**/*.{ts,tsx}',
6 | './components/**/*.{ts,tsx}',
7 | './app/**/*.{ts,tsx}',
8 | './src/**/*.{ts,tsx}',
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: "2rem",
14 | screens: {
15 | "2xl": "1400px",
16 | },
17 | },
18 | extend: {
19 | colors: {
20 | border: "hsl(var(--border))",
21 | input: "hsl(var(--input))",
22 | ring: "hsl(var(--ring))",
23 | background: "hsl(var(--background))",
24 | foreground: "hsl(var(--foreground))",
25 | primary: {
26 | DEFAULT: "hsl(var(--primary))",
27 | foreground: "hsl(var(--primary-foreground))",
28 | },
29 | secondary: {
30 | DEFAULT: "hsl(var(--secondary))",
31 | foreground: "hsl(var(--secondary-foreground))",
32 | },
33 | destructive: {
34 | DEFAULT: "hsl(var(--destructive))",
35 | foreground: "hsl(var(--destructive-foreground))",
36 | },
37 | muted: {
38 | DEFAULT: "hsl(var(--muted))",
39 | foreground: "hsl(var(--muted-foreground))",
40 | },
41 | accent: {
42 | DEFAULT: "hsl(var(--accent))",
43 | foreground: "hsl(var(--accent-foreground))",
44 | },
45 | popover: {
46 | DEFAULT: "hsl(var(--popover))",
47 | foreground: "hsl(var(--popover-foreground))",
48 | },
49 | card: {
50 | DEFAULT: "hsl(var(--card))",
51 | foreground: "hsl(var(--card-foreground))",
52 | },
53 | },
54 | borderRadius: {
55 | lg: "var(--radius)",
56 | md: "calc(var(--radius) - 2px)",
57 | sm: "calc(var(--radius) - 4px)",
58 | },
59 | keyframes: {
60 | "accordion-down": {
61 | from: { height: 0 },
62 | to: { height: "var(--radix-accordion-content-height)" },
63 | },
64 | "accordion-up": {
65 | from: { height: "var(--radix-accordion-content-height)" },
66 | to: { height: 0 },
67 | },
68 | },
69 | animation: {
70 | "accordion-down": "accordion-down 0.2s ease-out",
71 | "accordion-up": "accordion-up 0.2s ease-out",
72 | },
73 | },
74 | },
75 | plugins: [require("tailwindcss-animate")],
76 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------