# Les 18 — Lesopdracht ## Kies één modaliteit, bouw een mini-demo **Duur:** 30 min in-class --- ## Doel Pak één van de modaliteiten uit deze les en bouw daar een minimale werkende demo van. Voice, vision, image gen, of local LLM — jouw keuze. --- ## Opties ### Optie 1 — Voice transcriptie Bouw een mic-button + transcribe. Werkende voice → text in een Next.js page. ```typescript // app/voice/page.tsx "use client"; import { useState, useRef } from "react"; export default function VoicePage() { const [text, setText] = useState(""); const [recording, setRecording] = useState(false); const recorder = useRef(null); const chunks = useRef([]); async function start() { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); recorder.current = new MediaRecorder(stream); recorder.current.ondataavailable = (e) => chunks.current.push(e.data); recorder.current.onstop = async () => { const blob = new Blob(chunks.current, { type: "audio/webm" }); chunks.current = []; const fd = new FormData(); fd.append("audio", blob); const res = await fetch("/api/transcribe", { method: "POST", body: fd }); const { text } = await res.json(); setText(text); }; recorder.current.start(); setRecording(true); } function stop() { recorder.current?.stop(); setRecording(false); } return (

{text}

); } ``` `app/api/transcribe/route.ts`: ```typescript import { experimental_transcribe as transcribe } from "ai"; import { openai } from "@ai-sdk/openai"; export async function POST(req: Request) { const fd = await req.formData(); const audio = fd.get("audio") as Blob; const result = await transcribe({ model: openai.transcription("whisper-1"), audio: new Uint8Array(await audio.arrayBuffer()), }); return Response.json({ text: result.text }); } ``` --- ### Optie 2 — Vision foto-analyse Bouw een upload-form + analyse. User upload foto → AI beschrijft. ```typescript // app/vision/page.tsx "use client"; import { useState } from "react"; export default function VisionPage() { const [desc, setDesc] = useState(""); const [loading, setLoading] = useState(false); async function analyze(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setLoading(true); const fd = new FormData(); fd.append("image", file); const res = await fetch("/api/analyze", { method: "POST", body: fd }); const { description } = await res.json(); setDesc(description); setLoading(false); } return (
{loading &&

Bezig...

}

{desc}

); } ``` `app/api/analyze/route.ts`: ```typescript import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; export async function POST(req: Request) { const fd = await req.formData(); const image = fd.get("image") as File; const buffer = await image.arrayBuffer(); const result = await generateText({ model: openai("gpt-4o"), messages: [{ role: "user", content: [ { type: "text", text: "Beschrijf wat er op deze foto staat in 3 zinnen." }, { type: "image", image: new Uint8Array(buffer) }, ], }], }); return Response.json({ description: result.text }); } ``` --- ### Optie 3 — Image generation Bouw een prompt-input → generate → preview. ```typescript // app/generate/page.tsx "use client"; import { useState } from "react"; export default function GeneratePage() { const [prompt, setPrompt] = useState(""); const [imageUrl, setImageUrl] = useState(""); const [loading, setLoading] = useState(false); async function generate() { setLoading(true); const res = await fetch("/api/generate", { method: "POST", body: JSON.stringify({ prompt }), }); const { image } = await res.json(); setImageUrl(`data:image/png;base64,${image}`); setLoading(false); } return (