AutomationFlowsWeb Scraping › Generate Sound Effect Variation Packs with Elevenlabs and Google Drive

Generate Sound Effect Variation Packs with Elevenlabs and Google Drive

ByKevin Yu @exekyute on n8n.io

This workflow collects a single sound-effect brief via an n8n form, generates multiple variation takes with the ElevenLabs sound-generation API, and uploads each MP3 into a timestamped Google Drive folder, then returns a results page with links and per-take status. Receives a…

Event trigger★★★★☆ complexity16 nodesForm TriggerGoogle DriveHTTP RequestForm
Web Scraping Trigger: Event Nodes: 16 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16954 — we link there as the canonical source.

This workflow follows the Form → Form Trigger recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Generate a sound effect variation pack from one brief using ElevenLabs and Google Drive",
  "tags": [],
  "nodes": [
    {
      "name": "When a Brief Is Submitted",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        48,
        288
      ],
      "parameters": {
        "options": {
          "appendAttribution": false
        },
        "formTitle": "Sound effect variation pack",
        "formFields": {
          "values": [
            {
              "fieldType": "textarea",
              "fieldLabel": "Description",
              "placeholder": "e.g. heavy wooden door creaking open slowly in an empty hall",
              "requiredField": true
            },
            {
              "fieldType": "number",
              "fieldLabel": "Number of takes",
              "placeholder": "1 to 5 (default 3)"
            },
            {
              "fieldType": "number",
              "fieldLabel": "Duration seconds",
              "placeholder": "0.5 to 30, leave blank to let ElevenLabs decide"
            },
            {
              "fieldType": "number",
              "fieldLabel": "Prompt influence",
              "placeholder": "0 to 1, leave blank for 0.3"
            }
          ]
        },
        "formDescription": "Describe one sound and get several ElevenLabs takes to choose from."
      },
      "typeVersion": 2.6
    },
    {
      "name": "Plan Takes and Folder",
      "type": "n8n-nodes-base.code",
      "position": [
        336,
        336
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const DEFAULT_TAKES = 3;\nconst MAX_TAKES = 5;\nconst MIN_DURATION = 0.5;\nconst MAX_DURATION = 30;\nconst DEFAULT_INFLUENCE = 0.3;\nconst INFLUENCE_STEP = 0.1;\nconst PARENT_FOLDER_ID = 'PASTE_YOUR_SFX_OUTPUT_FOLDER_ID_HERE';\n\nconst clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));\nconst form = $input.first().json;\n\nconst brief = String(form['Description'] || '').trim();\n\nlet takes = Math.round(Number(form['Number of takes']) || DEFAULT_TAKES);\ntakes = clamp(takes, 1, MAX_TAKES);\n\nlet duration = null;\nconst rawDuration = form['Duration seconds'];\nif (rawDuration !== '' && rawDuration != null && !isNaN(Number(rawDuration))) {\n  duration = clamp(Number(rawDuration), MIN_DURATION, MAX_DURATION);\n}\n\nlet baseInfluence = DEFAULT_INFLUENCE;\nconst rawInfluence = form['Prompt influence'];\nif (rawInfluence !== '' && rawInfluence != null && !isNaN(Number(rawInfluence))) {\n  baseInfluence = clamp(Number(rawInfluence), 0, 1);\n}\n\nconst slug = brief.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'sound-effect';\nconst now = new Date();\nconst pad = (x) => String(x).padStart(2, '0');\nconst stamp = now.getFullYear() + pad(now.getMonth() + 1) + pad(now.getDate()) + '-' + pad(now.getHours()) + pad(now.getMinutes()) + pad(now.getSeconds());\nconst folderName = slug + '-' + stamp;\n\nconst list = [];\nfor (let i = 0; i < takes; i++) {\n  const influence = clamp(Number((baseInfluence + i * INFLUENCE_STEP).toFixed(3)), 0, 1);\n  list.push({ takeNumber: i + 1, prompt: brief, duration: duration, prompt_influence: influence });\n}\n\nreturn [{ json: { brief: brief, totalTakes: takes, duration: duration, baseInfluence: baseInfluence, folderName: folderName, parentFolderId: PARENT_FOLDER_ID, takes: list } }];",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "name": "Create Take Folder in Drive",
      "type": "n8n-nodes-base.googleDrive",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        528,
        336
      ],
      "parameters": {
        "name": "={{ $json.folderName }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {
          "simplifyOutput": true
        },
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.parentFolderId }}"
        },
        "resource": "folder",
        "operation": "create",
        "authentication": "oAuth2"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 3,
      "waitBetweenTries": 5000
    },
    {
      "name": "Build Take Requests",
      "type": "n8n-nodes-base.code",
      "position": [
        752,
        256
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const plan = $('Plan Takes and Folder').first().json;\nconst takes = Array.isArray(plan.takes) ? plan.takes : [];\n\nreturn takes.map((t) => {\n  const body = { text: t.prompt, prompt_influence: t.prompt_influence };\n  if (t.duration != null) body.duration_seconds = t.duration;\n  return {\n    json: {\n      takeNumber: t.takeNumber,\n      totalTakes: plan.totalTakes,\n      prompt: t.prompt,\n      prompt_influence: t.prompt_influence,\n      duration: t.duration,\n      bodyJson: JSON.stringify(body),\n    },\n    pairedItem: { item: 0 },\n  };\n});",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "name": "Generate Sound Effect",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1104,
        336
      ],
      "parameters": {
        "url": "https://api.elevenlabs.io/v1/sound-generation",
        "method": "POST",
        "options": {
          "timeout": 120000,
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "data"
            }
          }
        },
        "jsonBody": "={{ $json.bodyJson }}",
        "sendBody": true,
        "sendQuery": true,
        "contentType": "json",
        "specifyBody": "json",
        "specifyQuery": "keypair",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "queryParameters": {
          "parameters": [
            {
              "name": "output_format",
              "value": "mp3_44100_128"
            }
          ]
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 4.4,
      "waitBetweenTries": 5000
    },
    {
      "name": "Upload Take to Drive",
      "type": "n8n-nodes-base.googleDrive",
      "onError": "continueErrorOutput",
      "maxTries": 3,
      "position": [
        1344,
        256
      ],
      "parameters": {
        "name": "=take-{{ $('Build Take Requests').item.json.takeNumber }}.mp3",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Create Take Folder in Drive').first().json.id }}"
        },
        "resource": "file",
        "operation": "upload",
        "authentication": "oAuth2",
        "inputDataFieldName": "data"
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "typeVersion": 3,
      "waitBetweenTries": 5000
    },
    {
      "name": "Record Successful Take",
      "type": "n8n-nodes-base.code",
      "position": [
        1584,
        240
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const file = $json;\nconst takeNumber = $('Build Take Requests').item.json.takeNumber;\nconst id = file.id || '';\nconst link = file.webViewLink || (id ? ('https://drive.google.com/file/d/' + id + '/view') : '');\nreturn {\n  json: {\n    takeNumber: takeNumber,\n    status: 'ok',\n    fileName: file.name || ('take-' + takeNumber + '.mp3'),\n    link: link,\n  },\n};",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "name": "Record Failed Take",
      "type": "n8n-nodes-base.code",
      "position": [
        1584,
        416
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let takeNumber = null;\ntry { takeNumber = $('Build Take Requests').item.json.takeNumber; } catch (e) {}\nif (takeNumber == null) takeNumber = ($json.takeNumber != null ? $json.takeNumber : null);\nconst err = $json.error;\nconst message = (err && (err.message || err.description)) || (typeof err === 'string' ? err : '') || 'Generation or upload failed';\nreturn {\n  json: {\n    takeNumber: takeNumber,\n    status: 'failed',\n    fileName: '',\n    link: '',\n    error: String(message),\n  },\n};",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "name": "Build Results Summary",
      "type": "n8n-nodes-base.code",
      "position": [
        1856,
        304
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const rows = $input.all().map((i) => i.json).filter(Boolean);\nrows.sort((a, b) => (a.takeNumber || 0) - (b.takeNumber || 0));\n\nconst plan = $('Plan Takes and Folder').first().json;\nconst brief = plan.brief || '';\nconst total = (plan.totalTakes != null ? plan.totalTakes : rows.length);\nconst folderId = $('Create Take Folder in Drive').first().json.id || '';\nconst folderUrl = folderId ? ('https://drive.google.com/drive/folders/' + folderId) : '';\n\nconst okRows = rows.filter((r) => r.status === 'ok');\nconst failedRows = rows.filter((r) => r.status !== 'ok');\nconst missing = Math.max(0, total - okRows.length - failedRows.length);\n\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\nconst listItems = rows.map((r) => {\n  if (r.status === 'ok') {\n    return '<li>Take ' + esc(r.takeNumber) + ': <a href=\"' + esc(r.link) + '\" target=\"_blank\" rel=\"noopener\">' + esc(r.fileName || ('take-' + r.takeNumber + '.mp3')) + '</a></li>';\n  }\n  return '<li>Take ' + esc(r.takeNumber) + ': failed (' + esc(r.error || 'unknown error') + ')</li>';\n}).join('');\n\nconst folderLine = folderUrl\n  ? '<p><strong>Folder:</strong> <a href=\"' + esc(folderUrl) + '\" target=\"_blank\" rel=\"noopener\">open the variation pack in Google Drive</a></p>'\n  : '<p>The Drive folder link is unavailable.</p>';\n\nlet countLine = okRows.length + ' of ' + total + ' takes generated';\nif (failedRows.length) countLine += ', ' + failedRows.length + ' failed';\nif (missing) countLine += ', ' + missing + ' did not report back';\ncountLine += '.';\n\nconst html = '<div style=\"font-family:system-ui,Segoe UI,Arial,sans-serif;max-width:640px;line-height:1.55;\">' +\n  '<h2>Your sound effect variation pack</h2>' +\n  '<p><strong>Brief:</strong> ' + esc(brief) + '</p>' +\n  '<p>' + countLine + '</p>' +\n  folderLine +\n  '<ul>' + listItems + '</ul>' +\n  '</div>';\n\nreturn [{ json: { html: html, folderUrl: folderUrl, okCount: okRows.length, failedCount: failedRows.length, missingCount: missing, total: total, results: rows } }];",
        "language": "javaScript"
      },
      "typeVersion": 2
    },
    {
      "name": "Show the Variation Pack",
      "type": "n8n-nodes-base.form",
      "position": [
        2064,
        304
      ],
      "parameters": {
        "operation": "completion",
        "respondWith": "showText",
        "responseText": "={{ $json.html }}"
      },
      "typeVersion": 2.5
    },
    {
      "name": "Explain Folder Problem",
      "type": "n8n-nodes-base.form",
      "position": [
        752,
        416
      ],
      "parameters": {
        "operation": "completion",
        "respondWith": "showText",
        "responseText": "<div style=\"font-family:system-ui,Segoe UI,Arial,sans-serif;max-width:640px;line-height:1.55;\"><h2>Output folder not ready</h2><p>The workflow could not create the Drive subfolder for this run. This usually means the output folder has not been set yet.</p><p>Open the <strong>Plan Takes and Folder</strong> node and set <code>PARENT_FOLDER_ID</code> to a real Google Drive folder ID, and make sure the Google Drive credential is connected on both Drive nodes. Then submit again.</p></div>"
      },
      "typeVersion": 2.5
    },
    {
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -496
      ],
      "parameters": {
        "width": 1264,
        "height": 572,
        "content": "## Generate a sound effect variation pack from one brief using ElevenLabs and Google Drive\n\n### How it works\n\n1. A user submits one sound brief and a take count from 1 to 5 through an n8n form.\n2. A code step clamps the inputs and prepares one request per take, and a dated Drive subfolder is created for the run.\n3. Each take calls the ElevenLabs sound-generation API on its own, and because the API has no seed, every call returns a different result.\n4. Each returned MP3 is uploaded into the subfolder as take-1.mp3 through take-N.mp3.\n5. The form shows a link to the folder plus a per-take list marking each take ok or failed.\n\n### Setup steps\n\n- [ ] Create an ElevenLabs Header Auth credential named ElevenLabs with header name xi-api-key.\n- [ ] Create a Google Drive OAuth2 credential and select it on both Drive nodes.\n- [ ] Open Plan Takes and Folder and paste your output folder ID into PARENT_FOLDER_ID.\n- [ ] Leave the workflow inactive until both credentials and the folder ID are set.\n\n### Customization\n\nChange the default and maximum take count, adjust the duration clamp, or change how prompt influence is spread across takes in the Plan Takes and Folder node."
      },
      "typeVersion": 1
    },
    {
      "name": "Sticky Collect",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 280,
        "height": 384,
        "content": "## Collect the brief\n\nOne form takes the sound description, the take count, and optional duration and influence."
      },
      "typeVersion": 1
    },
    {
      "name": "Sticky Plan",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        272,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 720,
        "height": 496,
        "content": "## Plan takes and make a folder\n\nClamp the inputs, build one request per take, and create a dated Drive subfolder for this run."
      },
      "typeVersion": 1
    },
    {
      "name": "Sticky Generate",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1024,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 732,
        "height": 472,
        "content": "## Generate and store each take\n\nCall ElevenLabs once per take for a different result, upload each MP3, and record ok or failed."
      },
      "typeVersion": 1
    },
    {
      "name": "Sticky Return",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1792,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 470,
        "height": 396,
        "content": "## Return the variation pack\n\nCollapse the take results and show the folder link plus a per-take status list."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Record Failed Take": {
      "main": [
        [
          {
            "node": "Build Results Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Take Requests": {
      "main": [
        [
          {
            "node": "Generate Sound Effect",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Take to Drive": {
      "main": [
        [
          {
            "node": "Record Successful Take",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Record Failed Take",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Results Summary": {
      "main": [
        [
          {
            "node": "Show the Variation Pack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Sound Effect": {
      "main": [
        [
          {
            "node": "Upload Take to Drive",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Record Failed Take",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Plan Takes and Folder": {
      "main": [
        [
          {
            "node": "Create Take Folder in Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Record Successful Take": {
      "main": [
        [
          {
            "node": "Build Results Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When a Brief Is Submitted": {
      "main": [
        [
          {
            "node": "Plan Takes and Folder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Take Folder in Drive": {
      "main": [
        [
          {
            "node": "Build Take Requests",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Explain Folder Problem",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow collects a single sound-effect brief via an n8n form, generates multiple variation takes with the ElevenLabs sound-generation API, and uploads each MP3 into a timestamped Google Drive folder, then returns a results page with links and per-take status. Receives a…

Source: https://n8n.io/workflows/16954/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

Generate SRT and VTT subtitle files from a media URL using Gladia and Google Drive. Uses formTrigger, httpRequest, googleDrive, form. Event-driven trigger; 16 nodes.

Form Trigger, HTTP Request, Google Drive +1
Web Scraping

This generate unique AI-powered music tracks using the ElevenLabs Music API.

Form Trigger, HTTP Request, Google Drive +1
Web Scraping

Generate royalty-free sound effects for all your projects: ASMR, YouTube videos, podcasts, and more. This workflow generates unique AI-powered sound effects using the ElevenLabs Sound Effects API.

HTTP Request, Google Drive, Form +1
Web Scraping

This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t

Execute Command, Read Write File, HTTP Request +3
Web Scraping

LDX hub All Services Demo. Uses formTrigger, httpRequest, form, n8n-nodes-ldxhub. Event-driven trigger; 53 nodes.

Form Trigger, HTTP Request, Form +1