You Might Also Like
Stack vs Queue Visualizer — Free HTML CSS JS Snippet
Stack vs Queue Visualizer · Animations · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Stack vs Queue Visualizer — Side-by-Side LIFO and FIFO Animation Sharing the Same Input Sequence

LIFO and FIFO are usually explained with two separate, unrelated diagrams, which makes it easy to recite "stacks are last-in-first-out, queues are first-in-first-out" without ever building an intuition for what that actually means for a real sequence of operations. This snippet fixes that by feeding the exact same sequence of pushed values into a Stack and a Queue simultaneously, then letting you remove from each independently — after a handful of operations the two structures visibly hold different remaining items in a different order, which is the entire concept made concrete instead of abstract.
One shared input, two independent backing arrays
Under the hood there are two completely separate JavaScript arrays, stack and queue, each holding { value, el } entries. The "Add random item to both" button generates a single random value and calls pushStack(value) and enqueueQueue(value) together via Promise.all, so both structures always receive an identical sequence of inputs in an identical order. Nothing about the insertion logic differs between them — pushStack appends to the end of the stack array and enqueueQueue appends to the end of the queue array, using Array.push() in both cases. The divergence that matters is entirely in how each structure is read back out.
Why popStack() and dequeueQueue() are the whole story
popStack() calls stack.pop(), which removes and returns the array's *last* element — the most recently added item, giving Last-In-First-Out order. dequeueQueue() calls queue.shift(), which removes and returns the array's *first* element — the earliest added item still present, giving First-In-First-Out order. That is the entire algorithmic difference between a stack and a queue: identical insertion, opposite-end removal. Everything else in this snippet exists purely to make that one-line distinction visible and memorable.
Absolute positioning instead of relying on flex reflow
Rather than letting the browser's normal flow reposition items after a removal (which snaps instantly with no animation), every item is an absolutely positioned .sq-item inside a position: relative container, and its bottom (stack) or left (queue) coordinate is recalculated by layoutStack()/layoutQueue() after every mutation. Because those properties have a CSS transition, changing them triggers a smooth animated move rather than an instant jump. This is a deliberate simplification worth calling out: the stack's items never actually need to move when you push or pop, because both operations only ever touch the top slot — only the queue's remaining items need repositioning after a dequeue, since every item shifts one slot toward the front. Watch closely and you'll notice the stack's untouched items are visually still, while the queue's items visibly slide left after every dequeue.
Enter and leave animations use the same two CSS classes in both boards
Both boards share an identical .entering/.leaving class pair: a new item starts at opacity: 0; transform: scale(0.6) and is un-classed on the next animation frame so the browser animates it up to full size and opacity; a removed item gets .leaving added (scaling back down and fading) and is only detached from the DOM after await wait(220), matching the CSS transition's duration so the element is never yanked out mid-animation. Using the same visual language for both structures is intentional — it keeps the comparison fair, so any difference you see in *behavior* is a difference in the algorithm, not in the animation styling.
The operation log as a plain-English audit trail
Every push, pop, enqueue, and dequeue writes a line to the scrolling .sq-log panel, phrased as the actual function call and its result (e.g. pop() -> C42 (removed from top)). After adding four or five items and alternately popping and dequeuing, scrolling back through this log next to the two boards is often what makes the LIFO/FIFO distinction finally click — you can see in plain text that the same input sequence produced two different, and fully explainable, output orders.
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 sequence of five pushes and three pops/dequeues you write out by hand, exactly which items each structure ends up holding — predicting it yourself first, then checking your prediction, is the fastest way to internalize LIFO versus FIFO. Good extensions to ask for: a "peek" button that highlights the next item each structure would return without removing it, a shared capacity limit that visually rejects further pushes once full, or a third board showing a double-ended queue (deque) that can add or remove from either end.
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:
Build a side-by-side stack and queue visualizer in plain HTML, CSS, and JavaScript, no libraries or frameworks.
Requirements:
- Two separate boards on the same page sharing the same underlying set of pushed values: a Stack (LIFO) shown as a vertical column, and a Queue (FIFO) shown as a horizontal row.
- A single shared "Add random item to both" action that generates one random value and adds it to both structures at the same time (push onto the stack, enqueue onto the queue), so both start from an identical input sequence.
- Implement the stack with array push/pop (removing from the end) and the queue with array push/shift (removing from the start) — the entire LIFO vs FIFO behavior should come from that one distinction, not from separate custom logic per structure.
- Separate Pop and Dequeue buttons, each acting only on its own structure, animating the removed item fading/scaling out from the correct end (top for the stack, front for the queue).
- Position items absolutely within each board and recompute every remaining item's position after a mutation so removals animate smoothly — the stack's untouched items should not need to move on push/pop, since only its top slot changes, while every remaining queue item should visibly shift toward the front after a dequeue.
- After several alternating operations, the two structures must end up holding a visibly different remaining set/order of items, making the LIFO-vs-FIFO distinction concrete rather than something the user has to take on faith.
- Include a scrolling operation history log that records every push, pop, enqueue, and dequeue as a short plain-English line, including a graceful no-op message when popping or dequeuing an empty structure.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
- 1Click "Add random item to both" a few timesEach click generates one random value and animates it into both the Stack (entering from the top) and the Queue (entering at the back/right) at the same time, using an identical value in both.
- 2Click Pop on the StackThe most recently added item animates out from the top of the column and disappears — pop always removes whatever was pushed last.
- 3Click Dequeue on the QueueThe oldest remaining item animates out from the front (left) of the row, and every remaining item slides one slot to the left to close the gap — dequeue always removes whatever was enqueued first.
- 4Add a couple more items, then Pop and Dequeue againKeep alternating. Because both structures started from the same input sequence but remove from opposite ends, they now hold visibly different sets of leftover items.
- 5Read the operation history logEvery push, pop, enqueue, and dequeue is recorded as a plain-English line, letting you trace exactly which values went in and which order they came back out in for each structure.
- 6Try emptying one structure completelyKeep clicking Pop or Dequeue until a board is empty and click once more — the log reports a clean "structure is empty" message instead of erroring, showing both operations are safe to call on an empty structure.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Yes. Keep the stack and queue plain arrays in refs (React), non-reactive instance variables (Vue), or class fields (Angular) rather than framework state, since they are mutated imperatively (push/pop/shift) and the DOM nodes are managed directly rather than re-rendered declaratively. Trigger pushStack/enqueueQueue/popStack/dequeueQueue from click handlers wired up in useEffect, onMounted, or ngAfterViewInit. The cleanup concern is the chained await wait(ms) calls: guard each with an "is mounted" flag checked before touching the DOM, and flip it false in the component unmount hook so an in-flight leave animation does not write to a node already removed by the framework.
A stack only ever adds or removes from one end — the top — so every item below the top stays in exactly the same slot regardless of how many push/pop operations happen. A queue removes from the opposite end it inserts at (front vs back), so every remaining item has to shift one slot closer to the front whenever the item ahead of it is dequeued. That structural difference is why this snippet visibly slides queue items on every dequeue but leaves stack items still on every pop.
A browser's back button and an undo feature in a text editor are classic stacks — the most recently visited page or the most recent edit is the first one reversed. A print spooler, a background job processor, and a customer support ticket queue are classic queues — the first job or ticket submitted is the first one handled, regardless of what gets added after it.
Array.shift() is O(n) in the worst case because every remaining element has to be re-indexed after the first one is removed, which is a known real-world performance caveat of array-backed queues at large scale — production job queues typically use a circular buffer or a linked list with head/tail pointers instead. This snippet intentionally keeps the implementation to plain arrays for the clearest possible read of the FIFO logic; the LRU cache visualizer in this library demonstrates the linked-list-based O(1) alternative for a comparable structure.
Only in the trivial case of a single item, or if you always remove everything from one structure before adding anything new. With three or more items pushed and only some of them removed, a stack and a queue fed the same input sequence will structurally diverge, since pop always takes the newest survivor while dequeue always takes the oldest one — that divergence is the whole point of running them side by side.