Screenplay Formatter
From plain text to perfect script.
Raw Script
Prefix lines: SCENE: for Scene Headings, ACTION: for Action. Character names in ALL CAPS are detected automatically. Parentheticals use ( ).
Formatted Preview
Your formatted script will appear here.
Type or paste your script on the left and see the magic happen.
${trimmedLine}
`; lastLineType = 'dialogue'; } }); return htmlOutput; } function updatePreview() { const rawText = rawTextInput.value; if (rawText.trim() === '') { placeholder.style.display = 'block'; formattedScriptContent.innerHTML = ''; downloadPdfBtn.disabled = true; return; } placeholder.style.display = 'none'; const formattedHtml = parseAndFormatScript(rawText); formattedScriptContent.innerHTML = formattedHtml; downloadPdfBtn.disabled = false; } // --- PDF GENERATION --- async function handlePdfDownload() { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'in', format: 'letter' }); pdf.setFont('Courier', 'normal'); pdf.setFontSize(12); const lines = rawTextInput.value.split('\n'); const pageHeight = 11; const topMargin = 1; const bottomMargin = 1; const leftMargin = 1.5; const rightMargin = 1; let y = topMargin; function checkPageBreak(lineHeight = 0.2) { if (y + lineHeight > pageHeight - bottomMargin) { pdf.addPage(); y = topMargin; } } lines.forEach(line => { const trimmedLine = line.trim(); if (trimmedLine === '') return; checkPageBreak(); if (trimmedLine.startsWith('SCENE:')) { pdf.text(trimmedLine.substring(6).trim().toUpperCase(), leftMargin, y); y += 0.4; // Extra space after heading } else if (trimmedLine.startsWith('ACTION:')) { pdf.text(trimmedLine.substring(7).trim(), leftMargin, y, { maxWidth: 6 }); y += (pdf.getTextDimensions(trimmedLine.substring(7).trim(), { maxWidth: 6 }).h / 72) + 0.2; } else if (trimmedLine.startsWith('(') && trimmedLine.endsWith(')')) { pdf.text(trimmedLine, 3.1, y); y += 0.2; } else if (trimmedLine === trimmedLine.toUpperCase() && trimmedLine.match(/^[A-Z\s]+$/)) { pdf.text(trimmedLine, 3.7, y); y += 0.2; } else { pdf.text(trimmedLine, 2.5, y, { maxWidth: 3.5 }); y += (pdf.getTextDimensions(trimmedLine, { maxWidth: 3.5 }).h / 72) + 0.2; } }); pdf.save('Formatted-Screenplay.pdf'); } // --- EVENT LISTENERS --- rawTextInput.addEventListener('input', updatePreview); downloadPdfBtn.addEventListener('click', handlePdfDownload); // --- INITIALIZATION --- lucide.createIcons(); updatePreview(); // Initial format on load });