As colin said, this is easy to achieve in a ui-template with the MediaRecorder API

NOTE: the MediaRecorder API requires a secure secure origin e.g. HTTPS or localhost
[{"id":"2a5f4c2cb6f860ad","type":"ui-template","z":"b4c00b43f02f573a","group":"7a30a31af6818ce4","page":"","ui":"","name":"","order":1,"width":0,"height":0,"head":"","format":"<template>\n <div class=\"d-flex flex-column align-center\">\n <v-btn color=\"red\" class=\"mb-4\" @click=\"startRecording\" :disabled=\"isRecording\">\n <v-icon left>mdi-microphone</v-icon>\n Start Recording\n </v-btn>\n <v-btn color=\"grey\" class=\"mb-4\" @click=\"stopRecording\" :disabled=\"!isRecording\">\n <v-icon left>mdi-stop</v-icon>\n Stop Recording\n </v-btn>\n <v-btn color=\"green\" class=\"mb-4\" @click=\"playback\" :disabled=\"!lastRecordingBlob\">\n <v-icon left>mdi-play</v-icon>\n Play Recording\n </v-btn>\n <v-icon :color=\"isRecording ? 'red' : 'grey'\" size=\"48\">\n {{ isRecording ? 'mdi-record-circle' : 'mdi-record-circle-outline' }}\n </v-icon>\n </div>\n</template>\n\n<script>\nexport default {\n data() {\n return {\n isRecording: false,\n mediaRecorder: null,\n audioChunks: [],\n lastRecordingBlob: null\n };\n },\n methods: {\n startRecording() {\n if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n alert('getUserMedia not supported in this browser.');\n return;\n }\n navigator.mediaDevices.getUserMedia({ audio: true })\n .then(stream => {\n this.isRecording = true;\n this.audioChunks = [];\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.ondataavailable = event => {\n if (event.data.size > 0) {\n this.audioChunks.push(event.data);\n }\n };\n this.mediaRecorder.onstop = () => {\n const audioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });\n this.lastRecordingBlob = audioBlob;\n // Send audio data as blob to node-red\n this.send({ payload: audioBlob });\n stream.getTracks().forEach(track => track.stop());\n this.mediaRecorder = null;\n };\n this.mediaRecorder.start();\n })\n .catch(err => {\n alert('Error accessing microphone: ' + err.message);\n });\n },\n stopRecording() {\n if (this.mediaRecorder && this.isRecording) {\n this.isRecording = false;\n this.mediaRecorder.stop();\n }\n },\n playback() {\n if (this.lastRecordingBlob) {\n const audioURL = URL.createObjectURL(this.lastRecordingBlob);\n const audio = new Audio(audioURL);\n audio.play();\n } else {\n alert('No recording available to play back.');\n }\n }\n }\n};\n</script>\n\n<style scoped>\n.v-btn {\n min-width: 180px;\n}\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":320,"y":200,"wires":[["68df473e351117a6"]]},{"id":"586f7deeb3d6102e","type":"ui-notification","z":"b4c00b43f02f573a","ui":"ba750d2403672175","position":"top right","colorDefault":true,"color":"#000000","displayTime":"3","showCountdown":true,"outputs":1,"allowDismiss":true,"dismissText":"Close","allowConfirm":false,"confirmText":"Confirm","raw":false,"className":"","name":"","x":950,"y":160,"wires":[[]]},{"id":"a74ff11982d7aa43","type":"debug","z":"b4c00b43f02f573a","name":"debug 20","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":920,"y":220,"wires":[]},{"id":"68df473e351117a6","type":"change","z":"b4c00b43f02f573a","name":"","rules":[{"t":"set","p":"recording","pt":"flow","to":"payload","tot":"msg","dc":true}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":200,"wires":[["961edd74b2774d1f"]]},{"id":"961edd74b2774d1f","type":"change","z":"b4c00b43f02f573a","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"Captured recording","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":720,"y":200,"wires":[["a74ff11982d7aa43","586f7deeb3d6102e"]]},{"id":"7a30a31af6818ce4","type":"ui-group","name":"Rec","page":"6db7daf1529deea2","width":6,"height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"ba750d2403672175","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":1,"showDisconnectNotification":true},{"id":"6db7daf1529deea2","type":"ui-page","name":"Recorder","ui":"ba750d2403672175","path":"/page4","icon":"home","layout":"grid","theme":"645ffb825f13937f","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":1,"className":"","visible":"true","disabled":"false"},{"id":"645ffb825f13937f","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"7c070d39e243656c","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.30.2"}}]