About drug abuse make a animated video
Animation video video
mkdir -p /home/daytona/workspace/drug-abuse-animation
~/drug-abuse-animation/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drug Abuse Awareness – Animated Story</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0a0a1a; font-family: 'Segoe UI', sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; }
h1 { color: #fff; font-size: 1.3rem; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 12px; opacity: 0.7; }
#stage {
width: 800px; height: 450px;
border-radius: 18px;
overflow: hidden;
position: relative;
box-shadow: 0 0 60px rgba(100,60,200,0.5);
background: #111;
}
canvas { display: block; }
#controls {
margin-top: 18px;
display: flex; gap: 14px; align-items: center;
}
button {
padding: 10px 28px; border: none; border-radius: 30px;
background: linear-gradient(135deg,#7b2ff7,#f107a3);
color: #fff; font-size: 1rem; cursor: pointer;
letter-spacing: 1px; transition: opacity .2s;
}
button:hover { opacity: .85; }
#scene-label {
color: #ccc; font-size: .85rem; letter-spacing: 1px;
min-width: 180px; text-align: center;
}
#progress-bar-wrap {
width: 800px; height: 6px; background: #222; border-radius: 3px; margin-top: 10px;
}
#progress-bar {
height: 100%; border-radius: 3px;
background: linear-gradient(90deg,#7b2ff7,#f107a3);
width: 0%; transition: width .3s;
}
</style>
</head>
<body>
<h1>🎬 Drug Abuse Awareness</h1>
<div id="stage"><canvas id="c" width="800" height="450"></canvas></div>
<div id="progress-bar-wrap"><div id="progress-bar"></div></div>
<div id="controls">
<button id="playBtn">▶ Play</button>
<span id="scene-label">Scene 1 / 8</span>
<button id="restartBtn">↺ Restart</button>
</div>
<script>
// ─── Canvas & context ────────────────────────────────────────────────────────
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
// ─── Utility helpers ─────────────────────────────────────────────────────────
function lerp(a,b,t){ return a+(b-a)*t; }
function easeInOut(t){ return t<.5?2*t*t:1-Math.pow(-2*t+2,2)/2; }
function easeOut(t){ return 1-Math.pow(1-t,3); }
// ─── Drawing primitives ──────────────────────────────────────────────────────
function drawSky(y1col, y2col){
const g=ctx.createLinearGradient(0,0,0,H);
g.addColorStop(0,y1col); g.addColorStop(1,y2col);
ctx.fillStyle=g; ctx.fillRect(0,0,W,H);
}
function drawGround(y, col){
ctx.fillStyle=col; ctx.fillRect(0,y,W,H-y);
}
function drawCloud(x,y,s,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle='#fff';
[[0,0,s],[s*.6,s*.1,s*.7],[s*1.2,0,s*.8],[s*.3,-s*.4,s*.6],[s*.9,-s*.35,s*.55]].forEach(([dx,dy,r])=>{
ctx.beginPath(); ctx.arc(x+dx,y+dy,r,0,Math.PI*2); ctx.fill();
});
ctx.restore();
}
function drawStar(x,y,r,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(x,y,r,0,Math.PI*2); ctx.fill();
ctx.restore();
}
// Draw a simple stick-person (x,y = feet center)
function drawPerson(x, y, scale, opts={}){
const {
shirtColor='#3498db', pantsColor='#2c3e50', skinColor='#f5cba7',
happy=true, sweat=false, flipped=false, alpha=1
} = opts;
ctx.save();
ctx.globalAlpha=alpha;
if(flipped){ ctx.translate(x,y); ctx.scale(-1,1); ctx.translate(-x,-y); }
const S=scale;
// Legs
ctx.strokeStyle=pantsColor; ctx.lineWidth=S*3;
ctx.beginPath(); ctx.moveTo(x,y-S*30); ctx.lineTo(x-S*12,y); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x,y-S*30); ctx.lineTo(x+S*12,y); ctx.stroke();
// Body
ctx.strokeStyle=shirtColor; ctx.lineWidth=S*4;
ctx.beginPath(); ctx.moveTo(x,y-S*65); ctx.lineTo(x,y-S*30); ctx.stroke();
// Arms
ctx.strokeStyle=shirtColor; ctx.lineWidth=S*3;
if(happy){
ctx.beginPath(); ctx.moveTo(x,y-S*55); ctx.lineTo(x-S*20,y-S*40); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x,y-S*55); ctx.lineTo(x+S*20,y-S*40); ctx.stroke();
} else {
ctx.beginPath(); ctx.moveTo(x,y-S*55); ctx.lineTo(x-S*18,y-S*65); ctx.stroke();
ctx.beginPath(); ctx.moveTo(x,y-S*55); ctx.lineTo(x+S*18,y-S*65); ctx.stroke();
}
// Head
ctx.fillStyle=skinColor; ctx.beginPath(); ctx.arc(x,y-S*80,S*15,0,Math.PI*2); ctx.fill();
ctx.strokeStyle='#c8a07a'; ctx.lineWidth=1; ctx.stroke();
// Eyes
ctx.fillStyle='#222';
ctx.beginPath(); ctx.arc(x-S*5,y-S*82,S*2.5,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+S*5,y-S*82,S*2.5,0,Math.PI*2); ctx.fill();
// Mouth
ctx.strokeStyle='#a0522d'; ctx.lineWidth=S*1.5;
ctx.beginPath();
if(happy){
ctx.arc(x,y-S*76,S*6,0.2,Math.PI-0.2); ctx.stroke();
} else {
ctx.arc(x,y-S*70,S*6,Math.PI+0.2,-0.2); ctx.stroke();
}
// Sweat drops
if(sweat){
ctx.fillStyle='#74b9ff';
ctx.beginPath(); ctx.arc(x+S*18,y-S*85,S*3,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+S*22,y-S*78,S*2,0,Math.PI*2); ctx.fill();
}
ctx.restore();
}
function drawBrain(x,y,s,col,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=col; ctx.strokeStyle='#8e44ad'; ctx.lineWidth=2;
ctx.beginPath();
ctx.ellipse(x,y,s*1.1,s*.8,0,0,Math.PI*2); ctx.fill(); ctx.stroke();
ctx.fillStyle='rgba(0,0,0,.15)';
// folds
for(let i=0;i<5;i++){
ctx.beginPath();
ctx.arc(x-s*.5+i*s*.25,y,s*.3,0,Math.PI,true);
ctx.stroke();
}
ctx.restore();
}
function drawPill(x,y,w,h,col,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=col; ctx.strokeStyle='rgba(0,0,0,.2)'; ctx.lineWidth=2;
ctx.beginPath();
const r=h/2;
ctx.moveTo(x+r,y); ctx.lineTo(x+w-r,y);
ctx.arc(x+w-r,y+r,r,-Math.PI/2,Math.PI/2);
ctx.lineTo(x+r,y+h);
ctx.arc(x+r,y+r,r,Math.PI/2,3*Math.PI/2);
ctx.closePath(); ctx.fill(); ctx.stroke();
ctx.restore();
}
function drawSyringe(x,y,angle,a){
ctx.save(); ctx.globalAlpha=a;
ctx.translate(x,y); ctx.rotate(angle);
ctx.fillStyle='#ecf0f1'; ctx.strokeStyle='#bdc3c7'; ctx.lineWidth=2;
ctx.fillRect(-40,-8,80,16); ctx.strokeRect(-40,-8,80,16);
ctx.fillStyle='#c0392b';
ctx.fillRect(-40,-8,30,16);
ctx.fillStyle='#7f8c8d';
ctx.beginPath(); ctx.moveTo(40,-4); ctx.lineTo(60,0); ctx.lineTo(40,4); ctx.closePath(); ctx.fill();
ctx.restore();
}
function drawCross(x,y,s,col,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=col;
ctx.fillRect(x-s*.15,y-s*.5,s*.3,s);
ctx.fillRect(x-s*.5,y-s*.15,s,s*.3);
ctx.restore();
}
function drawHeart(x,y,s,col,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=col;
ctx.beginPath();
ctx.moveTo(x,y+s*.4);
ctx.bezierCurveTo(x,y,x-s,y,x-s,y-s*.4);
ctx.bezierCurveTo(x-s,y-s,x,y-s*.9,x,y-s*.5);
ctx.bezierCurveTo(x,y-s*.9,x+s,y-s,x+s,y-s*.4);
ctx.bezierCurveTo(x+s,y,x,y,x,y+s*.4);
ctx.closePath(); ctx.fill();
ctx.restore();
}
function drawTree(x,y,s,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle='#5d4037'; ctx.fillRect(x-s*.08,y-s*.3,s*.16,s*.3);
ctx.fillStyle='#2e7d32';
ctx.beginPath(); ctx.arc(x,y-s*.5,s*.35,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x-s*.2,y-s*.3,s*.25,0,Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(x+s*.2,y-s*.3,s*.25,0,Math.PI*2); ctx.fill();
ctx.restore();
}
function drawBuilding(x,y,w,h,col,a){
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=col;
ctx.fillRect(x,y,w,h);
ctx.fillStyle='rgba(255,255,180,0.6)';
for(let row=0;row<4;row++) for(let col2=0;col2<3;col2++){
ctx.fillRect(x+8+col2*(w/3.2), y+10+row*((h-10)/4.5), w/5, h/7);
}
ctx.restore();
}
function writeText(text, x, y, size, col, align, bold, alpha){
ctx.save();
ctx.globalAlpha = alpha !== undefined ? alpha : 1;
ctx.fillStyle=col;
ctx.font = `${bold?'bold ':''}${size}px Segoe UI, Arial`;
ctx.textAlign=align||'center';
ctx.fillText(text, x, y);
ctx.restore();
}
function wrapText(text, x, y, maxW, lineH, size, col, alpha){
ctx.save();
ctx.globalAlpha = alpha !== undefined ? alpha : 1;
ctx.fillStyle=col;
ctx.font=`${size}px Segoe UI, Arial`;
ctx.textAlign='center';
const words=text.split(' '), lines=[];
let cur='';
words.forEach(w=>{
const test=cur?cur+' '+w:w;
if(ctx.measureText(test).width>maxW){ lines.push(cur); cur=w; }
else cur=test;
});
lines.push(cur);
lines.forEach((l,i)=>ctx.fillText(l,x,y+i*lineH));
ctx.restore();
}
function drawSubtitleBox(text, progress){
if(!text) return;
ctx.save();
ctx.fillStyle='rgba(0,0,0,0.6)';
ctx.beginPath();
const bw=700, bh=52, bx=(W-bw)/2, by=H-75;
ctx.roundRect(bx,by,bw,bh,10);
ctx.fill();
ctx.fillStyle='#fff';
ctx.font='bold 18px Segoe UI, Arial';
ctx.textAlign='center';
ctx.fillText(text, W/2, by+34);
ctx.restore();
}
// ─── Stars (for night scenes) ─────────────────────────────────────────────
const stars = Array.from({length:80},()=>({
x:Math.random()*W, y:Math.random()*H*.5,
r:Math.random()*1.5+0.3, twinkle:Math.random()*Math.PI*2
}));
function drawStars(t){
stars.forEach(s=>{
drawStar(s.x,s.y,s.r,(Math.sin(s.twinkle+t*.02)*.3+.7)*.8);
});
}
// ─── Particle system ─────────────────────────────────────────────────────────
class Particle {
constructor(x,y,vx,vy,col,life,r){
this.x=x;this.y=y;this.vx=vx;this.vy=vy;
this.col=col;this.life=life;this.maxLife=life;this.r=r;
}
update(){
this.x+=this.vx; this.y+=this.vy;
this.vy+=.15; this.life--;
}
draw(){
const a=this.life/this.maxLife;
ctx.save(); ctx.globalAlpha=a;
ctx.fillStyle=this.col;
ctx.beginPath(); ctx.arc(this.x,this.y,this.r,0,Math.PI*2); ctx.fill();
ctx.restore();
}
}
let particles = [];
function spawnParticles(x,y,col,n,speed,life,r){
for(let i=0;i<n;i++){
const angle=Math.random()*Math.PI*2;
const v=Math.random()*speed+1;
particles.push(new Particle(x,y,Math.cos(angle)*v,Math.sin(angle)*v-2,col,life,r||4));
}
}
// ─── Scene definitions ───────────────────────────────────────────────────────
// Each scene: { duration (frames), draw(t, progress 0-1) }
const SCENES = [
// ── Scene 0: Title ──────────────────────────────────────────────────────
{
duration: 180,
subtitle: 'Drug Abuse: Know the Truth',
draw(t, p){
drawSky('#0d1b2a','#1a3a5c');
drawStars(t);
// big glowing title
const pulse = Math.sin(t*.05)*.05+.95;
ctx.save();
ctx.shadowColor='#f107a3'; ctx.shadowBlur=30;
writeText('DRUG ABUSE', W/2, H/2-40, 72*pulse, '#fff', 'center', true, easeOut(p));
ctx.shadowColor='#7b2ff7'; ctx.shadowBlur=20;
writeText('KNOW THE TRUTH', W/2, H/2+30, 30, '#f1c40f', 'center', true, easeOut(Math.max(0,p-.3)/.7));
ctx.restore();
writeText('An Awareness Story', W/2, H/2+80, 20, '#aaa', 'center', false, easeOut(Math.max(0,p-.6)/.4));
// floating pills
for(let i=0;i<4;i++){
const px=100+i*190, py=60+Math.sin(t*.03+i)*15;
drawPill(px,py,50,20,['#e74c3c','#3498db','#2ecc71','#f39c12'][i],0.3);
}
}
},
// ── Scene 1: Happy life ──────────────────────────────────────────────────
{
duration: 200,
subtitle: 'Alex was a happy, healthy student with big dreams.',
draw(t, p){
// daytime sky
drawSky('#87ceeb','#c9e8ff');
// sun
ctx.save(); ctx.shadowColor='#ffeb3b'; ctx.shadowBlur=40;
ctx.fillStyle='#ffeb3b'; ctx.beginPath(); ctx.arc(680,80,45,0,Math.PI*2); ctx.fill();
ctx.restore();
drawCloud(80,80,.4,0.85);
drawCloud(400,60,.5,0.7);
drawGround(320,'#4caf50');
// school building
drawBuilding(120, 180, 180, 140, '#ecf0f1', easeOut(p));
writeText('SCHOOL', 210, 240, 16, '#34495e', 'center', true, easeOut(p));
// trees
drawTree(360, 320, 80, easeOut(p));
drawTree(420, 320, 60, easeOut(p));
// happy Alex walking in
const alexX = lerp(-60, 260, easeInOut(Math.min(1, p*2)));
drawPerson(alexX, 320, 1.6, {happy:true, shirtColor:'#3498db', alpha:easeOut(p)});
// school bag (simple rect)
ctx.save(); ctx.globalAlpha=easeOut(p);
ctx.fillStyle='#e74c3c';
ctx.fillRect(alexX+12, 320-85, 20, 28); ctx.restore();
// birds
for(let b=0;b<3;b++){
const bx=(200+b*120+t*.8)%820-20;
const by=100+b*20+Math.sin(t*.06+b)*8;
ctx.save(); ctx.strokeStyle='#333'; ctx.lineWidth=2;
ctx.beginPath(); ctx.moveTo(bx,by); ctx.quadraticCurveTo(bx+8,by-6,bx+16,by); ctx.stroke();
ctx.restore();
}
}
},
// ── Scene 2: Peer pressure ──────────────────────────────────────────────
{
duration: 220,
subtitle: 'One day, a friend offered Alex a drug — "Just try it once!"',
draw(t, p){
drawSky('#2c3e50','#4a235a');
drawGround(330,'#1a1a1a');
drawStars(t);
// street lamp
ctx.save(); ctx.strokeStyle='#f39c12'; ctx.lineWidth=4;
ctx.beginPath(); ctx.moveTo(600,100); ctx.lineTo(600,330); ctx.stroke();
ctx.fillStyle='#f1c40f'; ctx.beginPath(); ctx.ellipse(600,105,20,8,0,0,Math.PI*2); ctx.fill();
ctx.save(); ctx.shadowColor='#f1c40f'; ctx.shadowBlur=40;
ctx.fillStyle='rgba(255,240,100,0.12)';
ctx.beginPath(); ctx.ellipse(600,220,100,200,0,0,Math.PI*2); ctx.fill();
ctx.restore(); ctx.restore();
// Alex (nervous)
const alexX = 320;
drawPerson(alexX, 330, 1.6, {happy:false, shirtColor:'#3498db', sweat:true, alpha:easeOut(p)});
// Bad friend
const friendX = lerp(800, 500, easeInOut(Math.min(1,p*1.5)));
drawPerson(friendX, 330, 1.6, {happy:true, shirtColor:'#e74c3c', skinColor:'#f5cba7', flipped:true, alpha:easeOut(p)});
// pill/offer floating
if(p > .4){
const pillA = easeOut((p-.4)/.6);
drawPill(alexX+80, 280, 50, 20, '#e74c3c', pillA);
writeText('Try it!', alexX+130, 265, 18, '#f1c40f', 'center', true, pillA);
// question mark over Alex
writeText('?', alexX-10, 235, 36, '#fff', 'center', true, pillA*.8);
}
// speech bubble from friend
if(p > .5){
const bx=friendX-120, by=230;
ctx.save(); ctx.globalAlpha=easeOut((p-.5)/.5);
ctx.fillStyle='#fff'; ctx.strokeStyle='#ccc'; ctx.lineWidth=2;
ctx.beginPath(); ctx.roundRect(bx-70,by-25,150,45,12); ctx.fill(); ctx.stroke();
ctx.beginPath(); ctx.moveTo(bx+30,by+20); ctx.lineTo(bx+50,by+42); ctx.lineTo(bx+15,by+20); ctx.closePath(); ctx.fill();
writeText('Just once!', bx+5, by+3, 16, '#c0392b', 'center', true);
ctx.restore();
}
}
},
// ── Scene 3: Brain effect ───────────────────────────────────────────────
{
duration: 220,
subtitle: 'Drugs flood the brain with dopamine — creating a false sense of pleasure.',
draw(t, p){
drawSky('#1a0033','#330055');
// Brain center stage
const bx=W/2, by=H/2-20;
const bs=90+Math.sin(t*.08)*8;
// glow
ctx.save();
ctx.shadowColor='#9b59b6'; ctx.shadowBlur=50+Math.sin(t*.06)*20;
drawBrain(bx, by, bs, '#e8b4f8', easeOut(p));
ctx.restore();
writeText('BRAIN', bx, by+bs+20, 18, '#ddd', 'center', true, easeOut(p));
// dopamine particles
if(p>.3){
const dp=easeOut((p-.3)/.7);
for(let i=0;i<8;i++){
const angle=(i/8)*Math.PI*2+t*.04;
const r=120+Math.sin(t*.05+i)*15;
const px2=bx+Math.cos(angle)*r, py2=by+Math.sin(angle)*r;
ctx.save(); ctx.globalAlpha=dp;
ctx.fillStyle='#f1c40f';
ctx.font='bold 22px Arial';
ctx.textAlign='center';
ctx.fillText('✦', px2, py2);
ctx.restore();
}
// dopamine label
writeText('DOPAMINE SURGE', W/2, 60, 24, '#f1c40f', 'center', true, dp);
// arrows into brain
for(let i=0;i<5;i++){
const angle=(i/5)*Math.PI*2+t*.02;
const x1=bx+Math.cos(angle)*(bs+80);
const y1=by+Math.sin(angle)*(bs+80);
const x2=bx+Math.cos(angle)*bs;
const y2=by+Math.sin(angle)*bs;
ctx.save(); ctx.globalAlpha=dp*.7;
ctx.strokeStyle='#9b59b6'; ctx.lineWidth=3;
ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2); ctx.stroke();
ctx.fillStyle='#9b59b6';
ctx.beginPath(); ctx.arc(x2,y2,5,0,Math.PI*2); ctx.fill();
ctx.restore();
}
}
// Warning note
if(p>.6){
const wa=easeOut((p-.6)/.4);
ctx.save(); ctx.globalAlpha=wa;
ctx.fillStyle='rgba(231,76,60,0.2)';
ctx.beginPath(); ctx.roundRect(30,H-130,W-60,70,10); ctx.fill();
wrapText('Over time the brain needs MORE drug to feel normal — this is ADDICTION',
W/2, H-100, 700, 24, 16, '#ff7675', wa);
ctx.restore();
}
}
},
// ── Scene 4: Downward spiral ─────────────────────────────────────────────
{
duration: 240,
subtitle: 'Alex started skipping school, losing friends, and falling apart.',
draw(t, p){
// gloomy sky
drawSky('#2d3436','#636e72');
drawGround(320,'#555');
// rain
for(let r=0;r<40;r++){
const rx=(r*97+t*4)%W;
const ry=((r*73+t*6))%(H*.8);
ctx.save(); ctx.globalAlpha=.3;
ctx.strokeStyle='#74b9ff'; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(rx,ry); ctx.lineTo(rx-3,ry+15); ctx.stroke();
ctx.restore();
}
// crumbling house
const cx=130;
ctx.save(); ctx.globalAlpha=easeOut(p);
ctx.fillStyle='#795548';
ctx.fillRect(cx,180,140,140);
ctx.fillStyle='#5d4037';
ctx.beginPath(); ctx.moveTo(cx-10,180); ctx.lineTo(cx+70,120); ctx.lineTo(cx+150,180); ctx.closePath(); ctx.fill();
// cracks
ctx.strokeStyle='#212121'; ctx.lineWidth=2;
ctx.beginPath(); ctx.moveTo(cx+40,200); ctx.lineTo(cx+55,240); ctx.lineTo(cx+48,270); ctx.stroke();
ctx.beginPath(); ctx.moveTo(cx+90,190); ctx.lineTo(cx+80,220); ctx.stroke();
ctx.restore();
// Alex sad, hunched
const alexX = 360;
// hunched by lowering y and making sad
drawPerson(alexX, 320 + Math.sin(t*.03)*3, 1.4, {
happy:false, shirtColor:'#636e72', sweat:false, alpha:easeOut(p)
});
// broken items around Alex
if(p>.3){
const ia=easeOut((p-.3)/.7);
// broken phone
ctx.save(); ctx.globalAlpha=ia;
ctx.fillStyle='#333'; ctx.fillRect(alexX+25, 285, 22, 36);
ctx.strokeStyle='#e74c3c'; ctx.lineWidth=1.5;
ctx.beginPath(); ctx.moveTo(alexX+30,295); ctx.lineTo(alexX+43,315); ctx.stroke();
ctx.beginPath(); ctx.moveTo(alexX+43,295); ctx.lineTo(alexX+30,315); ctx.stroke();
ctx.restore();
// Report card F
ctx.save(); ctx.globalAlpha=ia;
ctx.fillStyle='#ecf0f1'; ctx.fillRect(alexX-55, 265, 36, 45);
writeText('F', alexX-37, 295, 26, '#e74c3c', 'center', true);
ctx.restore();
}
// Crossed-out friends
if(p>.5){
const fa=easeOut((p-.5)/.5);
drawPerson(580, 320, 1.2, {happy:false, shirtColor:'#aaa', alpha:fa*.4});
drawPerson(650, 320, 1.2, {happy:false, shirtColor:'#aaa', alpha:fa*.4});
drawCross(580, 270, 40, 'rgba(231,76,60,.7)', fa);
drawCross(650, 270, 40, 'rgba(231,76,60,.7)', fa);
}
// pills on ground
for(let i=0;i<5;i++){
drawPill(alexX+50+i*30, 310, 28, 12, ['#e74c3c','#3498db','#9b59b6','#e74c3c','#2ecc71'][i], easeOut(p)*.6);
}
}
},
// ── Scene 5: Health consequences ────────────────────────────────────────
{
duration: 220,
subtitle: 'Drug abuse destroys your body, mind, and future.',
draw(t, p){
drawSky('#1a1a2e','#16213e');
// Split screen – left: body, right: stats
// Left side: deteriorating person
const pulse=Math.sin(t*.1)*.1;
drawPerson(200, 340, 1.5+pulse, {
happy:false, shirtColor:'#636e72', skinColor:'#d5a29a', sweat:true,
alpha:easeOut(p)
});
// heartbeat line erratic
ctx.save(); ctx.globalAlpha=easeOut(p);
ctx.strokeStyle='#e74c3c'; ctx.lineWidth=2;
ctx.beginPath();
const hbStart=60, hbY=H-100;
ctx.moveTo(hbStart, hbY);
const hbData=[0,0,0,-30,30,-15,10,0,0,0,0,-5,5,0,-20,25,-10,5,0,0];
hbData.forEach((dy,i)=>ctx.lineTo(hbStart+i*16, hbY+dy));
ctx.stroke();
ctx.restore();
writeText('♥ Irregular heartbeat', 210, H-55, 14, '#ff7675', 'center', false, easeOut(p));
// Right side consequences
const consList = [
{icon:'🧠', text:'Brain damage', col:'#a29bfe'},
{icon:'💉', text:'HIV / Hepatitis risk', col:'#fd79a8'},
{icon:'🫁', text:'Lung & liver failure', col:'#fdcb6e'},
{icon:'💔', text:'Heart disease', col:'#e74c3c'},
{icon:'😰', text:'Mental illness', col:'#74b9ff'},
];
consList.forEach((c,i)=>{
const cy=90+i*62, ca=easeOut(Math.max(0,(p-i*.08-.1)/.5));
ctx.save(); ctx.globalAlpha=ca;
ctx.fillStyle='rgba(255,255,255,.08)';
ctx.beginPath(); ctx.roundRect(420, cy-26, 320, 50, 8); ctx.fill();
ctx.font='22px Arial'; ctx.textAlign='left'; ctx.fillText(c.icon, 435, cy+8);
writeText(c.text, 580, cy+8, 18, c.col, 'center', true, 1);
ctx.restore();
});
}
},
// ── Scene 6: Help & Recovery ─────────────────────────────────────────────
{
duration: 220,
subtitle: 'It is never too late. Help is available — reach out!',
draw(t, p){
drawSky('#0a3d62','#1e3799');
drawGround(330,'#27ae60');
// sunrise/hope
const sunY=80-easeOut(p)*30;
ctx.save(); ctx.shadowColor='#f1c40f'; ctx.shadowBlur=60;
ctx.fillStyle='#f1c40f'; ctx.beginPath(); ctx.arc(W/2,sunY+20,50,0,Math.PI*2); ctx.fill();
ctx.restore();
// rays
for(let r=0;r<12;r++){
const angle=(r/12)*Math.PI*2+t*.01;
ctx.save(); ctx.globalAlpha=.15*easeOut(p);
ctx.strokeStyle='#f1c40f'; ctx.lineWidth=3;
ctx.beginPath();
ctx.moveTo(W/2+Math.cos(angle)*55, sunY+20+Math.sin(angle)*55);
ctx.lineTo(W/2+Math.cos(angle)*90, sunY+20+Math.sin(angle)*90);
ctx.stroke(); ctx.restore();
}
// doctor / counselor
drawPerson(320, 330, 1.6, {happy:true, shirtColor:'#fff', skinColor:'#f5cba7', alpha:easeOut(p)});
// stethoscope (simple arc)
ctx.save(); ctx.globalAlpha=easeOut(p);
ctx.strokeStyle='#3498db'; ctx.lineWidth=3;
ctx.beginPath(); ctx.arc(320, 330-60, 18, -.5, Math.PI+.5); ctx.stroke();
ctx.restore();
writeText('Doctor / Counselor', 320, H-10, 15, '#aaa', 'center', false, easeOut(p));
// Alex recovering
const alexX=500;
const alexY=330-easeOut(Math.max(0,p-.3)/.7)*10;
drawPerson(alexX, alexY, 1.6, {happy: p>.5, shirtColor:'#2ecc71', alpha:easeOut(Math.max(0,p-.1)/.9)});
// heart growing
if(p>.4){
const ha=easeOut((p-.4)/.6);
drawHeart(alexX, alexY-130, 20*ha, '#e74c3c', ha);
}
// support sign
if(p>.5){
const sa=easeOut((p-.5)/.5);
ctx.save(); ctx.globalAlpha=sa;
ctx.fillStyle='#2ecc71'; ctx.strokeStyle='#27ae60'; ctx.lineWidth=2;
ctx.beginPath(); ctx.roundRect(590,140,180,80,10); ctx.fill(); ctx.stroke();
writeText('REHAB', 680, 178, 20, '#fff', 'center', true);
writeText('We support you', 680, 205, 14, '#d5f5e3', 'center', false);
// pole
ctx.strokeStyle='#795548'; ctx.lineWidth=4;
ctx.beginPath(); ctx.moveTo(680,220); ctx.lineTo(680,330); ctx.stroke();
ctx.restore();
}
// falling confetti/stars (hope)
if(p>.6){
for(let i=0;i<15;i++){
const cx2=(i*137.5+t*2)%W;
const cy2=((i*83+t*3)%(H*.7));
ctx.save(); ctx.globalAlpha=.5;
ctx.fillStyle=['#f1c40f','#2ecc71','#3498db','#9b59b6','#e74c3c'][i%5];
ctx.beginPath(); ctx.arc(cx2,cy2,4,0,Math.PI*2); ctx.fill();
ctx.restore();
}
}
}
},
// ── Scene 7: Say NO / CTA ────────────────────────────────────────────────
{
duration: 240,
subtitle: 'Say NO to drugs. Choose life. Choose YOUR future.',
draw(t, p){
drawSky('#0d1b2a','#1a3a5c');
drawStars(t);
// big NO
const noScale=easeOut(p)*1+.3;
ctx.save();
ctx.translate(W/2, H/2-30);
ctx.scale(noScale, noScale);
ctx.shadowColor='#e74c3c'; ctx.shadowBlur=50;
ctx.fillStyle='#e74c3c';
ctx.font='bold 110px Segoe UI, Arial';
ctx.textAlign='center';
ctx.fillText('SAY NO', 0, 0);
ctx.restore();
// to drugs
writeText('to drugs', W/2, H/2+50, 38, '#f1c40f', 'center', true, easeOut(Math.max(0,p-.2)/.8));
// three happy people at bottom
if(p>.4){
const ga=easeOut((p-.4)/.6);
drawPerson(W/2-140, H-40, 1.5, {happy:true, shirtColor:'#3498db', alpha:ga});
drawPerson(W/2, H-40, 1.5, {happy:true, shirtColor:'#2ecc71', alpha:ga});
drawPerson(W/2+140, H-40, 1.5, {happy:true, shirtColor:'#9b59b6', alpha:ga});
}
// hotline box
if(p>.6){
const ha=easeOut((p-.6)/.4);
ctx.save(); ctx.globalAlpha=ha;
ctx.fillStyle='rgba(255,255,255,.12)';
ctx.beginPath(); ctx.roundRect(W/2-200,H-175,400,55,10); ctx.fill();
writeText('📞 Helpline: 1-800-662-4357 (SAMHSA)', W/2, H-142, 16, '#74b9ff', 'center', true);
ctx.restore();
}
// confetti celebration
particles.forEach(pp=>{ pp.update(); pp.draw(); });
particles=particles.filter(pp=>pp.life>0);
if(p>.5 && Math.random()<.4){
const col=['#f1c40f','#2ecc71','#3498db','#e74c3c','#9b59b6'][Math.floor(Math.random()*5)];
spawnParticles(Math.random()*W, -10, col, 2, 3, 60, 5);
}
}
}
];
// ─── Animation engine ─────────────────────────────────────────────────────
let currentScene = 0;
let sceneFrame = 0;
let totalFrame = 0;
let running = false;
let animId = null;
const totalFrames = SCENES.reduce((s,sc)=>s+sc.duration, 0);
function render(){
const scene = SCENES[currentScene];
const progress = sceneFrame / scene.duration;
ctx.clearRect(0,0,W,H);
scene.draw(sceneFrame, Math.min(1, progress));
drawSubtitleBox(scene.subtitle, progress);
// scene number overlay
ctx.save(); ctx.globalAlpha=.5;
ctx.fillStyle='rgba(0,0,0,.4)';
ctx.beginPath(); ctx.roundRect(14,14,110,30,8); ctx.fill();
writeText(`Scene ${currentScene+1} / ${SCENES.length}`, 69, 34, 14, '#ccc', 'center', false);
ctx.restore();
// progress bar
document.getElementById('progress-bar').style.width =
((totalFrame/totalFrames)*100)+'%';
document.getElementById('scene-label').textContent =
`Scene ${currentScene+1} / ${SCENES.length}`;
}
function tick(){
sceneFrame++;
totalFrame++;
if(sceneFrame > SCENES[currentScene].duration){
sceneFrame=0;
currentScene++;
if(currentScene >= SCENES.length){
currentScene = SCENES.length-1;
sceneFrame = SCENES[currentScene].duration;
running=false;
document.getElementById('playBtn').textContent='▶ Play';
render();
return;
}
}
render();
if(running) animId=requestAnimationFrame(tick);
}
function play(){
running=true;
document.getElementById('playBtn').textContent='⏸ Pause';
animId=requestAnimationFrame(tick);
}
function pause(){
running=false;
cancelAnimationFrame(animId);
document.getElementById('playBtn').textContent='▶ Play';
}
function restart(){
pause();
currentScene=0; sceneFrame=0; totalFrame=0;
particles=[];
render();
}
document.getElementById('playBtn').addEventListener('click',()=>{
if(running) pause(); else play();
});
document.getElementById('restartBtn').addEventListener('click',()=>{
restart();
});
// initial frame
render();
</script>
</body>
</html>
ls -lh /home/daytona/workspace/drug-abuse-animation/
index
HTML File · HTML
| Scene | Title | What happens |
|---|---|---|
| 1 | Title Screen | Glowing title with floating pills, intro text |
| 2 | Happy Life | Alex the stick-figure student walks to school on a sunny day |
| 3 | Peer Pressure | A friend offers a drug at night - "Just try it once!" |
| 4 | Brain Effect | Animated brain with dopamine surge, explains addiction mechanism |
| 5 | Downward Spiral | Rain, broken report card, lost friends, pills on the ground |
| 6 | Health Consequences | Split screen - erratic heartbeat + 5 health effects listed |
| 7 | Help & Recovery | Doctor + recovering Alex, sunrise, rehab sign |
| 8 | Say NO | Bold "SAY NO" with confetti celebration, SAMHSA helpline |