Playground Component
Check Palindrome
in Linked List Visualizer
An interactive palindrome linked list checker that finds the middle, reverses the second half, and compares both sides step by step.

Palindrome Linked List Checker
Example: Odd Palindrome — [1, 2, 3, 2, 1]
Phase
Idle
Slow
—
Fast
—
Step
0
Action
-
Status
Ready
Linked List
Idle
HeadNULL
1
2
3
2
1
Algorithm
1slow = fast = head2while (fast && fast.next) {3 slow = slow.next; fast = fast.next.next4}5reverse(second half from slow)6compare(head, reversedHead)
Copy Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Palindrome Linked List Checker</title>
<style>
:root{--bg:#090909;--panel:#111;--gold:#f4c430;--gold-dark:#b88700;--text:#f5f5f5;--border:#2f2f2f;--blue:#4ea8ff;--red:#ff6b6b;--green:#25c05a}*{box-sizing:border-box}body{margin:0}.pal-ll{min-height:100vh;padding:40px;background:radial-gradient(circle at top,#1a1505 0%,#090909 40%);color:var(--text);font-family:Poppins,Arial,sans-serif}.pal-ll h2{text-align:center;font-size:3rem;color:var(--gold);margin:0 0 8px}.pal-ll-subtitle{text-align:center;color:#999;margin:0 0 30px;font-size:.95rem}.pal-ll-controls{display:flex;justify-content:center;gap:15px;flex-wrap:wrap;margin-bottom:35px}.pal-ll-button{padding:14px 22px;border:0;border-radius:12px;cursor:pointer;font-weight:600;font-size:15px;background:linear-gradient(180deg,#ffd54d,#d9a900);color:#111;display:inline-flex;align-items:center;gap:10px;transition:.25s}.pal-ll-button:hover{transform:translateY(-2px)}.pal-ll-button:disabled{cursor:not-allowed;opacity:.55;transform:none}.pal-ll-button.secondary{background:#1c1c1c;color:var(--text);border:1px solid #333}.pal-ll-stats{width:100%;max-width:900px;margin:auto auto 40px;display:grid;grid-template-columns:1fr 1fr 1fr 1fr 2fr 1fr;gap:18px}.pal-ll-stat-box{background:#151515;border:1px solid #2b2b2b;border-radius:14px;padding:18px;text-align:center}.pal-ll-stat-box h4{margin:0;color:#999;font-size:.9rem}.pal-ll-stat-box p{margin:8px 0 0;color:var(--gold);font-size:1.4rem;font-weight:bold}.pal-ll-result{width:100%;max-width:900px;margin:0 auto 30px;padding:16px 22px;border-radius:14px;display:flex;align-items:center;justify-content:center;gap:12px;font-size:1.2rem;font-weight:700;border:1px solid;animation:pal-pop .35s ease}.pal-ll-result.is-yes{background:rgba(37,192,90,.1);border-color:var(--green);color:var(--green)}.pal-ll-result.is-no{background:rgba(255,107,107,.1);border-color:var(--red);color:var(--red)}@keyframes pal-pop{from{transform:scale(.94);opacity:0}to{transform:scale(1);opacity:1}}.pal-ll-code,.pal-ll-stage{width:100%;max-width:1400px;margin:0 auto 30px;padding:24px 26px 20px;border:1px solid var(--border);border-radius:18px;background:#111}.pal-ll-code h3,.pal-ll-stage h3{margin:0 0 16px;text-align:center;color:var(--gold);font-size:1.6rem;font-weight:700}.pal-ll-code-block{margin:0;font-family:"Fira Code","Courier New",monospace;font-size:1rem;line-height:1.9;background:#0c0c0c;border:1px solid #222;border-radius:12px;padding:16px 10px}.pal-ll-code-line{display:flex;align-items:center;gap:16px;padding:2px 14px;border-left:3px solid transparent;border-radius:6px;color:#8a8a8a;white-space:pre;transition:.3s}.pal-ll-code-linenum{color:#4a4a4a;font-size:.85rem;min-width:16px;text-align:right;user-select:none}.pal-ll-code-line.active{color:var(--gold);background:rgba(244,196,48,.1);border-left-color:var(--gold);transform:translateX(4px);animation:pal-glow 1.1s ease-in-out infinite}.pal-ll-code-line.active .pal-ll-code-linenum{color:var(--gold)}@keyframes pal-glow{50%{box-shadow:0 0 18px rgba(244,196,48,.32) inset,0 0 20px rgba(244,196,48,.28)}}.pal-ll-stage{padding:28px 30px 34px}.pal-ll-stage h3{margin-bottom:6px}.pal-ll-phase{text-align:center;color:#888;font-size:.85rem;margin:0 0 22px;text-transform:uppercase;letter-spacing:1px}.pal-ll-chain{display:flex;align-items:center;justify-content:center;gap:10px;min-height:132px;overflow-x:auto;padding:8px 4px 22px}.pal-ll-label,.pal-ll-null{color:var(--gold);font-weight:700;font-size:1rem;white-space:nowrap}.pal-ll-node-wrap{display:flex;flex-direction:column;align-items:center;gap:8px;min-width:82px;overflow:visible}.pal-ll-pointer-stack{width:100%;height:44px;display:flex;align-items:flex-end;justify-content:center;flex-wrap:wrap;gap:4px;overflow:visible}.pal-ll-pointer-badge{position:relative;z-index:2;min-width:34px;min-height:18px;padding:2px 5px;border:1px solid #343434;border-radius:5px;background:#111;color:#999;display:inline-flex;align-items:center;justify-content:center;font-size:.68rem;font-weight:700;line-height:1;text-transform:uppercase;transition:.3s}.slow-pointer{border-color:var(--blue);color:#91c8ff}.fast-pointer{border-color:var(--red);color:#ffaaa8}.left-pointer{border-color:var(--blue);color:#91c8ff;box-shadow:0 0 10px rgba(78,168,255,.35)}.right-pointer{border-color:var(--red);color:#ffaaa8;box-shadow:0 0 10px rgba(255,107,107,.35)}.pal-ll-node{width:72px;height:72px;border-radius:14px;background:#171717;border:2px solid #2c2c2c;color:var(--text);display:flex;align-items:center;justify-content:center;font-size:1.3rem;font-weight:700;transition:.25s}.pal-ll-node.slow{border-color:var(--blue)}.pal-ll-node.fast{border-color:var(--red)}.pal-ll-node.mid,.pal-ll-node.current{background:#ffb000;color:#000;border-color:#ffd54d;transform:translateY(-8px) scale(1.05);box-shadow:0 0 20px rgba(255,193,7,.45)}.pal-ll-node.reversed{border-color:#43e97b}.pal-ll-node.moving{transform:translateY(-12px) scale(1.05)}.pal-ll-node.compareLeft{border-color:var(--blue);box-shadow:0 0 16px rgba(78,168,255,.4);transform:translateY(-6px)}.pal-ll-node.compareRight{border-color:var(--red);box-shadow:0 0 16px rgba(255,107,107,.4);transform:translateY(-6px)}.pal-ll-node.match,.pal-ll-node.done{background:#25c05a;color:#fff;border-color:#43e97b;box-shadow:0 0 22px rgba(37,192,90,.35)}.pal-ll-node.mismatch{background:#c0293b;color:#fff;border-color:#ff6b6b;box-shadow:0 0 22px rgba(255,107,107,.45)}.pal-ll-arrow{position:relative;width:78px;height:22px;flex:0 0 78px;transition:transform .45s ease,opacity .35s ease}.pal-ll-arrow:before{content:"";position:absolute;left:0;right:13px;top:50%;height:4px;border-radius:999px;background:var(--gold);transform:translateY(-50%)}.pal-ll-arrow:after{content:"";position:absolute;right:0;top:50%;border-top:11px solid transparent;border-bottom:11px solid transparent;border-left:17px solid var(--gold);transform:translateY(-50%)}.pal-ll-arrow.backward{transform:rotate(180deg)}.pal-ll-arrow.cut{opacity:.22}.pal-ll-dot{width:20px;height:20px;border-radius:50%;background:var(--gold-dark);box-shadow:0 0 18px rgba(244,196,48,.3)}@media(max-width:768px){.pal-ll{padding:20px}.pal-ll h2{font-size:2rem}.pal-ll-stats{grid-template-columns:repeat(2,1fr)}.pal-ll-stage,.pal-ll-code{padding:18px 14px 14px}.pal-ll-code-block{font-size:.85rem;line-height:1.7}.pal-ll-code-line{gap:10px;padding:2px 8px}.pal-ll-node{width:58px;height:58px;font-size:1rem}.pal-ll-node-wrap{min-width:58px}.pal-ll-chain{justify-content:flex-start}.pal-ll-arrow{width:56px;flex-basis:56px}}
</style>
</head>
<body><main class="pal-ll"><h2>Palindrome Linked List Checker</h2><p id="subtitle" class="pal-ll-subtitle"></p><div class="pal-ll-controls"><button id="checkBtn" class="pal-ll-button">↻ Check Palindrome</button><button id="pauseBtn" class="pal-ll-button">❙❙ Pause</button><button id="speedBtn" class="pal-ll-button"></button><button id="exampleBtn" class="pal-ll-button secondary">⇄ Example</button><button id="resetBtn" class="pal-ll-button secondary">Reset</button></div><div id="stats" class="pal-ll-stats"></div><div id="result"></div><section class="pal-ll-stage"><h3>Linked List</h3><p id="phaseText" class="pal-ll-phase">Idle</p><div id="chain" class="pal-ll-chain"></div></section><section class="pal-ll-code"><h3>Algorithm</h3><pre id="code" class="pal-ll-code-block"></pre></section></main>
<script>
const examples=[{label:"Odd Palindrome",values:[1,2,3,2,1]},{label:"Even Palindrome",values:[4,8,8,4]},{label:"Not a Palindrome",values:[1,2,3,4,5]}];
const speedLevels=[["Very Slow",900],["Slow",650],["Normal",450],["Fast",260],["Very Fast",120]];
const codeLines=["slow = fast = head","while (fast && fast.next) {"," slow = slow.next; fast = fast.next.next","}","reverse(second half from slow)","compare(head, reversedHead)"];
const lineMap={"Init slow & fast":0,"Check fast / fast.next":1,"Move slow":2,"Move fast":2,"Middle found":3,"Cut arrow":4,"Flip arrow":4,"Reversing second half":4,"Compare values":5,Match:5,Mismatch:5,"Move left":5,"Move right":5};
let exampleIndex=0,values=examples[0].values,nodes,links,speedIndex=2,phase="idle",slowIndex=null,fastIndex=null,leftIndex=null,rightIndex=null,movingIndex=null,step=0,action="-",status="Ready",result=null,running=false,paused=false;
const phaseLabels={idle:"Idle",middle:"Find Middle",reverse:"Reverse Half",compare:"Compare",done:"Done"};
const sleep=ms=>new Promise(async resolve=>setTimeout(async()=>{while(paused)await new Promise(r=>setTimeout(r,120));resolve()},ms));
function buildNodes(list){return list.map(value=>({value,state:"default"}))}
function pointerValue(index){return index===null||index===undefined?"-":values[index]}
function reset(force=false,nextValues=values){if(running&&!force)return;paused=false;values=nextValues;nodes=buildNodes(values);links=Array(values.length-1).fill("forward");phase="idle";slowIndex=null;fastIndex=null;leftIndex=null;rightIndex=null;movingIndex=null;step=0;action="-";status="Ready";result=null;running=false;render()}
function markNode(index,state){nodes=nodes.map((node,i)=>i===index?{...node,state}:node)}
function setLinkState(index,state){links=links.map((link,i)=>i===index?state:link)}
function nodeClass(index){const classes=["pal-ll-node",nodes[index].state];if(index===slowIndex)classes.push("slow");if(index===fastIndex)classes.push("fast");if(index===leftIndex)classes.push("left");if(index===rightIndex)classes.push("right");if(index===movingIndex)classes.push("moving");return classes.join(" ")}
function pointerBadges(index){let html="";if(index===slowIndex&&phase==="middle")html+=`<span class="pal-ll-pointer-badge slow-pointer">Slow</span>`;if(index===fastIndex&&phase==="middle")html+=`<span class="pal-ll-pointer-badge fast-pointer">Fast</span>`;if(index===leftIndex&&phase==="compare")html+=`<span class="pal-ll-pointer-badge left-pointer">L</span>`;if(index===rightIndex&&phase==="compare")html+=`<span class="pal-ll-pointer-badge right-pointer">R</span>`;return html}
function render(){
const phaseLabel=phaseLabels[phase],compareMode=phase==="compare"||phase==="done";
subtitle.textContent=`Example: ${examples[exampleIndex].label} - [${values.join(", ")}]`;
checkBtn.disabled=running||status==="Completed";pauseBtn.disabled=!running;pauseBtn.innerHTML=paused?"▶ Play":"❙❙ Pause";speedBtn.textContent="Speed: "+speedLevels[speedIndex][0];exampleBtn.disabled=running;
stats.innerHTML=[["Phase",phaseLabel],[compareMode?"Left":"Slow",compareMode?pointerValue(leftIndex):pointerValue(slowIndex)],[compareMode?"Right":"Fast",compareMode?pointerValue(rightIndex):pointerValue(fastIndex)],["Step",step],["Action",action],["Status",status]].map(item=>`<div class="pal-ll-stat-box"><h4>${item[0]}</h4><p>${item[1]}</p></div>`).join("");
resultEl=document.getElementById("result");resultEl.innerHTML=result?`<div class="pal-ll-result ${result==="Palindrome"?"is-yes":"is-no"}"><span>${result==="Palindrome"?"✓":"✗"}</span>${result==="Palindrome"?"It IS a palindrome!":"It is NOT a palindrome"}</div>`:"";
phaseText.textContent=phaseLabel;
let chainHtml=`<span class="pal-ll-label">Head</span><span class="pal-ll-arrow"></span>`;
values.forEach((_,i)=>{chainHtml+=`<div class="pal-ll-node-wrap"><div class="pal-ll-pointer-stack">${pointerBadges(i)}</div><div class="${nodeClass(i)}">${nodes[i].value}</div></div>`;if(i<values.length-1)chainHtml+=`<span class="pal-ll-arrow ${links[i]}"></span>`});
chain.innerHTML=chainHtml+`<span class="pal-ll-arrow"></span><span class="pal-ll-dot"></span><span class="pal-ll-null">NULL</span>`;
const active=running||status==="Completed"?lineMap[action]:null;
code.innerHTML=codeLines.map((line,i)=>`<div class="pal-ll-code-line ${active===i?"active":""}"><span class="pal-ll-code-linenum">${i+1}</span><span class="pal-ll-code-text">${line}</span></div>`).join("");
}
async function checkPalindrome(){
if(running)return;reset(true,values);running=true;status="Checking";const n=values.length;let currentStep=0;phase="middle";let slow=0,fast=0;slowIndex=slow;fastIndex=fast;markNode(slow,"slow");action="Init slow & fast";step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);
while(fast<n&&fast+1<n){action="Check fast / fast.next";step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);markNode(slow,"default");slow+=1;fast+=2;slowIndex=slow;fastIndex=fast<n?fast:null;movingIndex=slow;markNode(slow,"slow");action="Move slow";step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);action="Move fast";movingIndex=null;render();await sleep(speedLevels[speedIndex][1])}
const mid=slow;action="Middle found";markNode(mid,"mid");fastIndex=null;render();await sleep(speedLevels[speedIndex][1]);
phase="reverse";slowIndex=null;render();
for(let i=n-2;i>=mid;i--){action="Cut arrow";setLinkState(i,"cut");movingIndex=i+1;markNode(i+1,"current");step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);action="Flip arrow";setLinkState(i,"backward");markNode(i+1,"reversed");markNode(i,i===mid?"mid":"default");movingIndex=null;step=++currentStep;render();await sleep(speedLevels[speedIndex][1])}
markNode(mid,"reversed");action="Reversing second half";render();await sleep(speedLevels[speedIndex][1]);
phase="compare";let left=0,right=n-1,palindrome=true;render();
while(left<right){leftIndex=left;rightIndex=right;markNode(left,"compareLeft");markNode(right,"compareRight");action="Compare values";step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);if(values[left]===values[right]){markNode(left,"match");markNode(right,"match");action="Match";step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);left+=1;right-=1;action="Move left";render();await sleep(speedLevels[speedIndex][1]);action="Move right";render();await sleep(speedLevels[speedIndex][1])}else{markNode(left,"mismatch");markNode(right,"mismatch");action="Mismatch";palindrome=false;step=++currentStep;render();await sleep(speedLevels[speedIndex][1]);break}}
leftIndex=null;rightIndex=null;phase="done";if(palindrome){nodes=nodes.map(node=>({...node,state:"done"}));result="Palindrome"}else{result="Not Palindrome"}action="Result";status="Completed";running=false;paused=false;render();
}
checkBtn.onclick=checkPalindrome;pauseBtn.onclick=()=>{if(!running)return;paused=!paused;status=paused?"Paused":"Checking";render()};speedBtn.onclick=()=>{speedIndex=(speedIndex+1)%speedLevels.length;render()};exampleBtn.onclick=()=>{if(running)return;exampleIndex=(exampleIndex+1)%examples.length;reset(true,examples[exampleIndex].values)};resetBtn.onclick=()=>reset();reset(true);
</script></body></html>
About this Component
A palindrome linked list reads the same forward and backward. Common solutions use slow and fast pointers to find the middle, reverse the second half, and compare values from both ends. This visualizer animates each phase: locating the middle, reversing the second half, and checking matching node values. The highlighted pseudocode follows the same steps as the animation. The efficient approach runs in O(n) time and uses O(1) extra space when the second half is reversed in place.
Explore More Components
Discover more components from the Playground.



