Binary Search Tree Visualizer — Free HTML CSS JS Snippet

Binary Search Tree Visualizer · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Real linked object graph (node.left / node.right pointers) — not an array or heap-style index, matching a textbook BST
Insert and search share one showCompare() step that highlights the current node and shows a live </>/= badge
Node x-position computed once at creation from parent depth, so existing nodes never need to move on later inserts
Failed search renders a dashed ghost node at the exact spot the value would be inserted, then removes it
Duplicate insert attempts are detected and rejected with a shake animation instead of silently corrupting the tree
In-order traversal computed with the textbook three-line recursive definition (left, node, right)
A CSS-transitioned marker dot visually flies between nodes in traversal order while a live sequence readout fills in
SVG line edges connect each node to its parent, drawn once at insert time with no re-layout pass required

About this UI Snippet

Binary Search Tree Visualizer — Animated Insert, Search & In-Order Traversal With Live Comparison Highlighting

Screenshot of the Binary Search Tree Visualizer snippet rendered live

A binary search tree diagram in a textbook is always shown fully built, which hides the one insight that actually explains the structure: every node's position is the result of a chain of less-than/greater-than comparisons made one at a time, starting from the root. This snippet makes that chain visible — insert and search both animate a descent through the tree, pausing at each node to show the comparison being made, before finally landing on an empty slot or a matching node.

The tree is a real object graph, not an array

root is a plain JavaScript object with left/right pointers to child node objects, built with makeNode(value, x, y, depth, parentId). There is no array-based heap-style indexing here — the structure is exactly what a computer science textbook means by "binary search tree," a graph of nodes linked by real object references, the same representation used for the linked list visualizer elsewhere in this library. insertValue and searchValue both walk this graph with a plain while (true) loop, reading cur.left/cur.right and reassigning cur to descend, exactly like a real BST implementation would.

Why insertion never has to reposition existing nodes

Every node's horizontal position is computed once, at creation, from spreadForDepth(cur.depth) — a function that returns a smaller spread the deeper a node sits (Math.max(2.6, 26 - depth * 5.2)), so a new child's x-coordinate is simply its parent's x-coordinate shifted left or right by that shrinking amount. Because this value only depends on the parent's fixed depth and fixed x, not on how many other nodes exist elsewhere in the tree, inserting a new node never requires recalculating or animating the position of any existing node — this is the same "layout narrows per subtree by depth" arithmetic used in the recursion tree visualizer, adapted here to a binary (not fan-out) tree. The practical benefit: insert() can just append one new DOM element and one new SVG edge without ever touching the rest of the tree.

showCompare(): the mechanism that makes the algorithm legible

Both insertValue and searchValue call the same showCompare(node, value) helper at every step of the descent. It highlights the current node amber, attaches a small floating badge showing the literal comparison result — <, >, or = — computed with value === node.value ? '=' : (value < node.value ? '<' : '>'), waits long enough for a human to read it, then removes the badge and highlight. This single function is what turns "the algorithm compares and branches" from a claim into something you watch happen, one node at a time, in the exact order the real comparisons occur.

Search failure renders the counterfactual insertion point

When searchValue walks off the tree (next is null) without finding a match, it does not just report failure — it computes the exact x/y position a real insert of that value would use (via the identical spreadForDepth math insertValue uses) and renders a dashed, red, question-marked "ghost" node there with a brief shake animation before removing it. This answers the natural follow-up question a "not found" result raises — *where would it have gone?* — instead of leaving it abstract.

In-order traversal: why left, node, right always yields sorted order

inorderList(node, out) is the textbook three-line recursive definition: recurse left, push the current node, recurse right. Because every left subtree in a valid BST contains only smaller values and every right subtree contains only larger ones, visiting in that exact order necessarily visits every node from smallest to largest — this is not a coincidence of implementation, it is the defining property of the BST invariant itself. runInorder() computes the full traversal order first, then animates a small red marker dot flying (via a CSS transition on left/top) to each node's position in that order, briefly enlarging the node and appending its value to a running sequence readout — so the printed output is a live, checkable proof that the tree really is sorted.

Comparison badges and the ghost node share one visual vocabulary

Every transient piece of feedback in this snippet — the amber active highlight during a comparison, the green pulse on a search hit, the red shake on a duplicate insert or a failed search — uses the same small set of CSS classes toggled temporarily and removed after a fixed await wait(ms), mirroring the pacing pattern used throughout this library's other algorithm visualizers rather than relying on a general-purpose animation library.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Give this snippet's JavaScript to an AI assistant like Claude and ask it to trace, for a specific value you pick, the exact sequence of </>/= comparisons insertValue would make before reaching an empty slot — predicting the path yourself first and then checking it is the fastest way to internalize how BST descent works. Worthwhile extensions to ask for: a delete(value) operation (the trickiest of the three, especially the two-children case), a balance factor readout that flags when the tree is degenerating toward a linked list, or pre-order and post-order traversal modes alongside the existing in-order one.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build an animated binary search tree visualizer in plain HTML, CSS, and JavaScript, no libraries or frameworks.

Requirements:
- Represent the tree as a real linked object graph (plain JS objects with left/right pointers to child nodes), not an array-backed heap index.
- Insert(value): animate a descent starting at the root, comparing the new value against each visited node (briefly highlighting that node and showing a </>/= indicator for the comparison result) and stepping left or right accordingly, until an empty child slot is found; then fade/scale a new node into that slot and draw a connecting edge to its parent. Reject and visually flag (e.g. a shake animation) an attempt to insert a value that already exists.
- Search(value): use the identical descend-and-highlight animation as insert, ending in either a clear "found" result (e.g. a green pulse on the matching node) or a clear "not found" result that also shows, at the exact empty slot the value would have occupied, a distinct placeholder/ghost node before removing it.
- Compute each node's horizontal position with plain arithmetic based on its parent's position and tree depth, so that a node's x-coordinate narrows (gets closer to its parent) at deeper levels and, critically, so that inserting a new node never requires recalculating or animating the position of any already-placed node.
- Include at least an in-order traversal button that visits nodes in the correct left-subtree, node, right-subtree recursive order, animating a visible marker moving between nodes with a brief pause on each, and prints the resulting value sequence — which must always come out sorted for a valid BST.
- Provide a numeric input plus Insert, Search, and Clear controls, and pre-populate the tree with a handful of values on load so the visualizer is not empty on first view.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Watch the pre-loaded tree build itselfThe tree starts with 50, 30, 70, 20, and 40 already inserted in sequence so you have something to search and traverse immediately.
  2. 2
    Type a value and click InsertThe descent highlights each node in amber with a floating </>/= badge showing the exact comparison, before the new node fades in at its correct empty slot with an edge drawn to its parent.
  3. 3
    Insert a value that already existsThe descent finds the matching node, gives it a brief red shake, and the status line explains that duplicates are not inserted — the tree structure does not change.
  4. 4
    Type a value and click SearchThe same node-by-node descent plays out. A match pulses green; a miss shows a dashed red ghost node at the exact empty slot the value would have occupied if it had been inserted.
  5. 5
    Click In-order traversalA small marker dot glides from node to node in left-subtree, node, right-subtree order, briefly enlarging each one, while the sequence readout below fills in with values left to right.
  6. 6
    Confirm the printed sequence is sortedRead the final in-order sequence and check it against the values you inserted — it will always come out in ascending order, which is the BST invariant made visible rather than asserted.

Real-world uses

Common Use Cases

Teaching binary search trees and the BST invariant
Makes the "left subtree smaller, right subtree larger" rule and the resulting sorted in-order traversal concrete and checkable rather than an assertion to memorize. Pairs well with the binary search visualizer for a fuller ordered-data teaching sequence.
Interview preparation for tree traversal and BST questions
BST insert, search, and the three traversal orders (in-order, pre-order, post-order) are extremely common interview topics. Watching the exact comparison at each descent step builds the intuition needed to write the recursive logic correctly under pressure.
Explaining sorted-set and ordered-map data structures
Many language standard libraries (Java TreeMap, C++ std::map, Python sortedcontainers) are backed by a self-balancing binary search tree. Use this visualizer to explain why operations like "find the next largest key" are efficient in those structures.
Interactive demo for a computer science course or blog post
Embed directly inside an article on trees or ordered data structures so readers can insert their own values and watch the descent and traversal live rather than reading a static diagram. Self-contained, no build step, fits any UI snippets gallery.
Onboarding material for engineers new to tree-based data structures
Use the synchronized compare-and-descend view during onboarding or mentoring to build a mental model for tree recursion before moving on to more complex balanced trees like AVL or red-black trees.
TAG
Debugging aid for a real BST or ordered-index implementation
Adapt the same showCompare-and-descend pattern to temporarily instrument a real tree-based index during development, confirming a suspiciously slow lookup is actually taking the expected O(log n) path rather than degrading toward a linear chain.

Got questions?

Frequently Asked Questions

Yes. Keep the root node graph in a ref (React), a non-reactive plain variable (Vue, mutated outside reactivity), or a class field (Angular), since it is a linked object structure manipulated imperatively rather than plain component state. Trigger insertValue/searchValue/runInorder from click handlers set up in useEffect, onMounted, or ngAfterViewInit. The cleanup concern is the chain of await wait(ms) calls inside every animated function: guard each checkpoint with an "is mounted" flag and set it false in the component unmount hook, so an in-progress descent, ghost-node display, or traversal marker flight does not keep writing to DOM nodes the framework has already removed.

Every node's x-position is derived only from its own parent's fixed x-position and its own fixed depth, via spreadForDepth(depth) — it never depends on how many sibling or cousin nodes exist elsewhere in the tree. Because that value is computed once, at the moment a node is created, and never recalculated afterward, adding a new leaf anywhere in the tree cannot change where any previously placed node sits — the layout is inherently stable under insertion.

It follows directly from the binary search tree invariant: at every node, everything in its left subtree is smaller and everything in its right subtree is larger. Visiting left-subtree, then the node itself, then right-subtree, recursively, therefore visits every value from smallest to largest by construction — it is not a special property of this implementation, it is the defining guarantee of a valid BST, which is exactly why this traversal order is the standard way to read a BST's contents back out as a sorted list.

The descent proceeds exactly as normal until it reaches the node whose value matches exactly (the "=" comparison badge appears), at which point insertValue stops, briefly shakes that node in red, and reports that the value already exists. No new node is created and no existing pointers are changed — this snippet's BST does not allow duplicate values, matching the most common textbook definition of a binary search tree.

spreadForDepth(depth) returns Math.max(2.6, 26 - depth * 5.2), a value that shrinks as depth increases and is clamped to a small minimum so nodes never fully overlap even very deep in the tree. This is purely a layout choice: since a full binary tree has exponentially more nodes competing for horizontal space at each deeper level, giving each level a smaller spread keeps the whole tree visually readable instead of children overlapping their neighbors.