Brain Dump Organizer (Auto-Sorts Ideas)

February 26, 2026 NEW

Here's a lightweight brain dump tool for story writers. You paste raw thoughts (messy is fine), hit one button, and it auto-organizes your notes into buckets like Characters, Plot, Worldbuilding, Scenes, Dialogue, and Loose Ideas.

Why it’s useful: The hardest part is getting the ideas out of your head. This tool is designed for speed: dump first, organize second. It doesn’t try to be a full writing app — it’s a fast “thinking scratchpad” that turns chaos into structure.

What it does:

  • Paste or type a brain dump (one thought per line, or paragraphs).
  • Auto-tags each line into categories using simple rules (keywords + patterns).
  • Highlights lines that look like character names, scene beats, or dialogue.
  • Saves everything in your browser (localStorage) — no database, no login.
  • Export organized notes to a single clean block you can paste into your doc.

Install:

  1. Create: /tools/story-organizer/
  2. Save as: /tools/story-organizer/index.html
  3. Visit: /tools/story-organizer/
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Story Brain Dump Organizer</title>
<meta name="description" content="Dump your story ideas and auto-organize them into characters, plot, world, scenes, dialogue, and loose ideas. Lightweight, no login." />
<meta name="robots" content="index,follow" />
<link rel="canonical" href="https://your-site.com/tools/story-organizer/" />

<style>
  :root{
    --bg:#0b0f16; --panel:#111a26; --panel2:#0c1420;
    --text:#e8eef8; --muted:rgba(232,238,248,.72);
    --line:rgba(255,255,255,.10); --accent:#2a8cff;
    --shadow:0 18px 55px rgba(0,0,0,.45); --r:16px;
    --ok:#7CFF9A; --warn:#FFD37C;
  }
  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--text);
       font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}
  .wrap{max-width:1200px;margin:0 auto;padding:18px}
  .card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r);
        padding:14px;margin:12px 0;box-shadow:var(--shadow)}
  .top{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:center}
  .pill{display:inline-block;padding:4px 10px;border-radius:999px;
        background:rgba(42,140,255,.14);border:1px solid rgba(42,140,255,.25);
        font-weight:900;font-size:12px}
  .muted{color:var(--muted)}
  .grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:12px}
  .c5{grid-column:span 5}
  .c7{grid-column:span 7}
  .c12{grid-column:span 12}
  @media(max-width:980px){.c5,.c7{grid-column:span 12}}
  textarea{
    width:100%;min-height:320px;resize:vertical;padding:10px 12px;
    border-radius:14px;border:1px solid var(--line);background:var(--panel2);
    color:var(--text);outline:none;
    font:650 13px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  }
  input{
    width:100%;padding:10px 12px;border-radius:12px;border:1px solid var(--line);
    background:var(--panel2);color:var(--text);outline:none;font-weight:800;
  }
  .btn{
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    border:0;border-radius:12px;padding:10px 12px;background:var(--accent);
    color:#06101a;font-weight:950;cursor:pointer;
  }
  .btn2{
    display:inline-flex;align-items:center;justify-content:center;gap:8px;
    border:1px solid var(--line);border-radius:12px;padding:10px 12px;background:transparent;
    color:var(--text);font-weight:950;cursor:pointer;
  }
  .row{display:flex;gap:10px;flex-wrap:wrap}
  .bucket{
    border:1px solid rgba(255,255,255,.10);
    background:rgba(255,255,255,.03);
    border-radius:16px;padding:12px;
  }
  .bucket h3{margin:0 0 8px;font-size:14px}
  .item{
    padding:8px 10px;border-radius:12px;border:1px solid rgba(255,255,255,.08);
    background:rgba(0,0,0,.18);margin:8px 0;white-space:pre-wrap;
  }
  .tag{
    display:inline-block;margin-left:8px;font-size:11px;font-weight:950;
    padding:2px 8px;border-radius:999px;border:1px solid rgba(255,255,255,.14);
    background:rgba(255,255,255,.05);color:var(--muted)
  }
  .hr{height:1px;background:var(--line);margin:12px 0}
  .small{font-size:12px}
  .mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
  .out{
    min-height:220px;padding:12px;border-radius:14px;border:1px solid var(--line);
    background:var(--panel2);white-space:pre-wrap;word-break:break-word;
    font:650 13px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  }
</style>
</head>
<body>
<div class="wrap">

  <div class="card">
    <div class="top">
      <div>
        <div class="pill">Writer Tool</div>
        <h1 style="margin:10px 0 6px;font-size:22px">Story Brain Dump Organizer</h1>
        <div class="muted">Dump ideas fast → auto-sort into buckets → export a clean outline.</div>
      </div>
      <div class="row">
        <button class="btn" id="organize" type="button">🧠 Organize</button>
        <button class="btn2" id="copyExport" type="button">📋 Copy Export</button>
        <button class="btn2" id="save" type="button">💾 Save</button>
        <button class="btn2" id="clear" type="button">🧹 Clear</button>
      </div>
    </div>

    <div class="hr"></div>

    <div class="grid">
      <div class="c5">
        <div class="muted small" style="margin-bottom:6px">Project title (optional)</div>
        <input id="title" placeholder="e.g. Neon Witch Detective" />
      </div>
      <div class="c7">
        <div class="muted small" style="margin-bottom:6px">Quick tip</div>
        <div class="muted small" style="line-height:1.5">
          One thought per line works best. Dialogue lines can start with quotes:
          <span class="mono">"I didn't come here to forgive you."</span>
        </div>
      </div>
    </div>
  </div>

  <div class="grid">
    <div class="card c5">
      <b>Brain dump</b>
      <div class="muted small" style="margin:6px 0 10px">
        Paste everything. Messy is the point.
      </div>
      <textarea id="dump" placeholder="She hides a map in a library book.
Main character: Riven, ex-paramedic turned smuggler.
The city is built inside a dead spaceship.
\"Don't say my name.\" (dialogue)
Ending: they realize the villain is future-them."></textarea>
    </div>

    <div class="card c7">
      <b>Organized buckets</b>
      <div class="muted small" style="margin:6px 0 10px">
        This uses simple rules (keywords + patterns). You can tweak the keyword lists in the script.
      </div>

      <div class="grid">
        <div class="c6">
          <div class="bucket">
            <h3>Characters</h3>
            <div id="b_char"></div>
          </div>
        </div>
        <div class="c6">
          <div class="bucket">
            <h3>Plot & Stakes</h3>
            <div id="b_plot"></div>
          </div>
        </div>

        <div class="c6">
          <div class="bucket">
            <h3>Worldbuilding</h3>
            <div id="b_world"></div>
          </div>
        </div>
        <div class="c6">
          <div class="bucket">
            <h3>Scenes & Beats</h3>
            <div id="b_scenes"></div>
          </div>
        </div>

        <div class="c6">
          <div class="bucket">
            <h3>Dialogue</h3>
            <div id="b_dialogue"></div>
          </div>
        </div>
        <div class="c6">
          <div class="bucket">
            <h3>Loose Ideas</h3>
            <div id="b_misc"></div>
          </div>
        </div>

        <div class="c12">
          <div class="bucket">
            <h3>Export (clean outline)</h3>
            <div class="out" id="export"></div>
          </div>
        </div>
      </div>

    </div>
  </div>

</div>

<script>
(function(){
  var KEY = "storyOrganizer:v1";

  function $(id){ return document.getElementById(id); }
  function lines(s){
    // split by lines, but also break big paragraphs into sentences-ish if needed
    var raw = String(s||"").replace(/\r/g,"").trim();
    if (!raw) return [];
    var arr = raw.split("\n").map(function(x){ return x.trim(); }).filter(Boolean);
    // if someone pasted one giant paragraph, try a gentle split
    if (arr.length <= 2 && raw.length > 240) {
      arr = raw.split(/(?<=[\.\!\?])\s+/).map(function(x){ return x.trim(); }).filter(Boolean);
    }
    return arr;
  }

  function uniq(arr){
    var seen = Object.create(null), out=[];
    arr.forEach(function(x){
      var k = x.toLowerCase();
      if (seen[k]) return;
      seen[k]=1; out.push(x);
    });
    return out;
  }

  // Simple rule sets (edit these to fit your vibe)
  var RULES = {
    characters: [
      "main character","protagonist","antagonist","villain","hero","sidekick","mentor",
      "sister","brother","mother","father","friend","rival",
      "named","character:","mc","npc"
    ],
    plot: [
      "goal","wants","must","stakes","conflict","problem","twist","reveal","betrayal",
      "ending","finale","climax","mystery","secret","deadline","plan","fails","wins","loss"
    ],
    world: [
      "world","setting","city","kingdom","planet","space","school","timeline","future","past",
      "magic","rules","technology","law","religion","culture","economy","species","faction"
    ],
    scenes: [
      "scene","chapter","beat","cold open","opening","montage","cut to","later","flashback",
      "fight","chase","heist","interrogation","break-in","escape","meet","kiss","argument"
    ]
  };

  function looksLikeDialogue(s){
    s = s.trim();
    if (!s) return false;
    if (s.startsWith('"') && s.endsWith('"')) return true;
    if (s.startsWith("“") && s.endsWith("”")) return true;
    // NAME: "..."
    if (/^[A-Z][A-Z0-9 _-]{1,20}:\s*["“]/.test(s)) return true;
    return false;
  }

  function looksLikeCharacterLine(s){
    // Heuristic: "Name:" or "Main character: X" or "Character: X"
    if (/^(main character|character|protagonist|antagonist)\s*:/i.test(s)) return true;
    // A single capitalized name with descriptor
    if (/^[A-Z][a-z]+(\s+[A-Z][a-z]+)?\s*[-–—:]/.test(s)) return true;
    return false;
  }

  function matchAny(s, words){
    var low = s.toLowerCase();
    for (var i=0;i<words.length;i++){
      if (low.indexOf(words[i]) !== -1) return true;
    }
    return false;
  }

  function tagLine(s){
    if (looksLikeDialogue(s)) return {bucket:"dialogue", tag:"dialogue"};
    if (looksLikeCharacterLine(s) || matchAny(s, RULES.characters)) return {bucket:"char", tag:"character"};
    if (matchAny(s, RULES.plot)) return {bucket:"plot", tag:"plot"};
    if (matchAny(s, RULES.world)) return {bucket:"world", tag:"world"};
    if (matchAny(s, RULES.scenes)) return {bucket:"scenes", tag:"scene"};
    return {bucket:"misc", tag:"idea"};
  }

  function renderBucket(id, arr, tag){
    var host = $(id);
    host.innerHTML = "";
    if (!arr.length){
      host.innerHTML = '<div class="muted small">—</div>';
      return;
    }
    arr.forEach(function(x){
      var div = document.createElement("div");
      div.className = "item";
      div.textContent = x;
      var t = document.createElement("span");
      t.className = "tag";
      t.textContent = tag;
      div.appendChild(t);
      host.appendChild(div);
    });
  }

  function buildExport(title, buckets){
    function section(name, arr){
      if (!arr.length) return "";
      return name + ":\n" + arr.map(function(x){ return "- " + x; }).join("\n") + "\n\n";
    }
    var out = "";
    if (title) out += title + "\n" + "=".repeat(Math.min(40, Math.max(6, title.length))) + "\n\n";
    out += section("Characters", buckets.char);
    out += section("Plot & Stakes", buckets.plot);
    out += section("Worldbuilding", buckets.world);
    out += section("Scenes & Beats", buckets.scenes);
    out += section("Dialogue", buckets.dialogue);
    out += section("Loose Ideas", buckets.misc);
    return out.trim();
  }

  function organize(){
    var t = $("title").value.trim();
    var dump = $("dump").value;

    var arr = lines(dump);
    arr = uniq(arr);

    var buckets = {char:[], plot:[], world:[], scenes:[], dialogue:[], misc:[]};

    arr.forEach(function(line){
      var r = tagLine(line);
      buckets[r.bucket].push(line);
    });

    // Render
    renderBucket("b_char", buckets.char, "character");
    renderBucket("b_plot", buckets.plot, "plot");
    renderBucket("b_world", buckets.world, "world");
    renderBucket("b_scenes", buckets.scenes, "scene");
    renderBucket("b_dialogue", buckets.dialogue, "dialogue");
    renderBucket("b_misc", buckets.misc, "idea");

    $("export").textContent = buildExport(t, buckets);
  }

  function save(){
    var obj = {title:$("title").value, dump:$("dump").value};
    localStorage.setItem(KEY, JSON.stringify(obj));
    alert("Saved (local).");
  }

  function load(){
    try{
      var j = JSON.parse(localStorage.getItem(KEY) || "");
      if (!j) return;
      $("title").value = j.title || "";
      $("dump").value = j.dump || "";
    } catch(e){}
  }

  function clearAll(){
    if (!confirm("Clear your brain dump?")) return;
    $("dump").value = "";
    $("export").textContent = "";
    ["b_char","b_plot","b_world","b_scenes","b_dialogue","b_misc"].forEach(function(id){
      $(id).innerHTML = '<div class="muted small">—</div>';
    });
  }

  function copyExport(){
    var txt = $("export").textContent || "";
    if (!txt.trim()) return;
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(txt).catch(function(){ fallbackCopy(txt); });
    } else fallbackCopy(txt);
  }
  function fallbackCopy(t){
    var ta = document.createElement("textarea");
    ta.value = t;
    document.body.appendChild(ta);
    ta.select();
    document.execCommand("copy");
    document.body.removeChild(ta);
  }

  $("organize").addEventListener("click", organize);
  $("save").addEventListener("click", save);
  $("clear").addEventListener("click", clearAll);
  $("copyExport").addEventListener("click", copyExport);

  load();
  // auto-organize when there’s content
  if ($("dump").value.trim()) organize();
})();
</script>
</body>
</html>

Comments (0)

No comments yet — be the first.

← Back to all scripts