Jupyter Notebook to Plain Text: Extraction Without the Scaffolding
Plain text is the right target when the destination has no opinion about formatting. A grep pipeline, a diff, a search index, a plagiarism check, a plain-text email to a colleague who does not want an attachment — none of these benefit from backticks and hash marks, and several of them are actively made worse by punctuation that means something to a renderer and nothing to the reader.
This converter takes an .ipynb and gives back the words and the code with no markup added.
Cells are separated by blank lines. Code appears as code, unfenced. Printed results appear beneath the
cell that produced them under a plain Output: label. Everything the JSON format wraps around
that content is discarded. The work happens in this browser tab.
The Token Argument
The most common reason to flatten a notebook is that something downstream charges by the token, and a raw
.ipynb is an expensive way to say very little. It is worth being concrete about the ratio.
A notebook with thirty cells, half of them producing a chart, routinely runs to two or three megabytes on disk. The prose and code inside it might be eight kilobytes. Everything else is base64 image data, HTML duplicates of results that also exist as plain text, and per-cell bookkeeping. Feeding the raw file to a model does not just cost a hundred times more than it should — it usually fails outright, because the context window fills with characters that carry no information.
Worse, base64 tokenizes terribly. Ordinary English averages roughly four characters per token. A random base64 run has no repeated substrings for the tokenizer's vocabulary to compress, so it lands closer to one token per two characters. The most useless part of the file is also the part with the worst token-per-character ratio. The counter above shows the token count of the result so you can see what you are actually sending.
What Survives the Flattening
Removing structure is not the same as removing meaning, and the line between them is where this kind of tool succeeds or fails. What is kept:
- Markdown cell content, as written. The
#and**characters stay, because in a markdown cell they are the author's only indication of emphasis and hierarchy. Stripping them would flatten a document that was already prose into undifferentiated text. - Code, exactly as typed. Indentation preserved, comments intact. Python's meaning depends on leading whitespace, so this is not a stylistic choice.
- Textual results. Printed output, returned values, dataframe previews in their plain form.
- Error names, messages and tracebacks, with the terminal colour codes removed.
- Raw cells, which usually hold LaTeX or reStructuredText the author wanted left alone.
What goes: image and video payloads, PDF attachments, the HTML twin of any result that also exists as text, execution counts, cell identifiers, empty metadata objects, and cells with nothing in them. A cell containing only whitespace and no output is not a cell anyone needs to read.
The Figure Placeholder Question
When a plot is removed, something has to mark the gap or not. There is a real tradeoff. Leave nothing and the text reads cleanly but a sentence like "as the chart above shows" now refers to nothing. Leave a placeholder and you have added a line that carries almost no information.
The default is to leave one short line naming the media type, and the switch above turns it off. There is
a related subtlety worth knowing about: matplotlib emits a text representation alongside the image, and
it is always something like <Figure size 640x480 with 1 Axes>. Kept naively you get
that line and the placeholder, one after the other, saying the same thing twice. When a figure has been
dropped, that particular text is suppressed.
Truncation From the Middle
Some outputs are long because they are informative and some are long because a loop printed inside it. The tool cannot tell the difference, so it caps each output and cuts from the middle, keeping the beginning and the end and marking how many characters went missing.
Middle truncation beats the alternative for a specific reason: the two most informative parts of a long output are almost always the start, which shows the shape of what was produced, and the end, which shows where it landed. A tail-truncated dataframe shows you the first rows and hides the summary line. A head-truncated one hides the column names. Cutting the middle keeps both ends of the story.
Practical Uses for a Flat Notebook
A few things get easier once the JSON is gone. Diffing two versions of a notebook in plain text shows what
changed in the analysis rather than which execution counts incremented — the reason
nbdime exists at all is that ordinary git diff on an .ipynb is
unreadable. Searching a folder of notebooks for the function that built a chart becomes a normal
grep. Counting words for a report, or feeding a notebook to a text-to-speech tool, or pasting
methodology into a document that does not render Markdown all work without further cleanup.
Privacy, and Why It Is Structural Here
Notebooks are working documents, and working documents accumulate things nobody meant to keep: a token pasted in while debugging an API, a connection string, twenty rows of customer data printed to verify a merge. Handing that to a conversion service means handing over whatever is in it.
The parser here is JavaScript running in this tab. Your file is read by the browser, converted in memory, and displayed. There is no upload, so there is no retention policy to trust and no deletion to request. If you would rather have fenced code blocks and Markdown headings, the notebook to Markdown converter does that with the same guarantee.