Code Sketch
atharva ghodke
Category: Programming
// ============================================================
// FUTURISTIC HOLOGRAM ROBOT - WALK AND SAY HI
// KOJO / SCALA
// ============================================================
cleari()
originBottomLeft()
class Sketch {
var t = 0.0
var bob = 0.0
var ringAngle = 0.0
var eyeGlow = 0.0
var coreGlow = 0.0
var walkPhase = 0.0
var waveAngle = 0.0
var waving = false
var walking = false
var robotX = cwidth / 2.0
var robotY = 205.0
var targetX = robotX
var targetY = robotY
var lookX = 0.0
var lookY = 0.0
var hiTimer = 0
var clicked = false
var clickX = 0.0
var clickY = 0.0
def clamp(v: Double, lo: Double, hi: Double): Double =
math.max(lo, math.min(hi, v))
def alpha(v: Double): Int =
clamp(v, 0.0, 255.0).toInt
// ----------------------------------------------------------
// BACKGROUND
// ----------------------------------------------------------
def drawBackground(c: CanvasDraw): Unit = {
c.background(5, 10, 28)
c.stroke(20, 75, 115, 65)
c.strokeWeight(1)
for (x <- 0 until cwidth.toInt by 35) {
c.line(x, 55, x, cheight)
}
for (y <- 55 until cheight.toInt by 35) {
c.line(0, y, cwidth, y)
}
for (i <- 0 until 45) {
val px =
(i * 97.0 + math.sin(t + i) * 12.0) % cwidth
val py =
60.0 +
((i * 61.0 + t * (12 + i % 5)) %
(cheight - 70))
val size = 2.0 + math.sin(t * 2.0 + i)
c.noStroke()
c.fill(0, 210, 255, 150)
c.ellipse(px, py, size, size)
}
c.noStroke()
c.fill(0, 130, 220, 35)
c.ellipse(cwidth / 2.0, 75, 360, 45)
c.fill(0, 190, 255, 25)
c.ellipse(cwidth / 2.0, 75, 250, 25)
}
// ----------------------------------------------------------
// HOLOGRAM PLATFORM
// ----------------------------------------------------------
def drawPlatform(c: CanvasDraw): Unit = {
val cx = robotX
val cy = 88.0
c.noFill()
c.stroke(0, 200, 255, 150)
c.strokeWeight(2)
c.ellipse(cx, cy, 190, 30)
c.ellipse(cx, cy, 145, 22)
c.stroke(0, 230, 255, 90)
c.ellipse(
cx, cy,
220 + math.sin(t * 2.0) * 10.0,
38
)
c.noStroke()
c.fill(0, 190, 255, 80)
c.ellipse(cx, cy, 90, 12)
}
// ----------------------------------------------------------
// ENERGY RINGS
// ----------------------------------------------------------
def drawRings(c: CanvasDraw): Unit = {
val cx = robotX
val cy = robotY + bob
c.noFill()
c.strokeWeight(2)
c.stroke(0, 230, 255, 130)
c.ellipse(
cx, cy + 20,
170 + math.sin(ringAngle) * 12,
38
)
c.stroke(100, 70, 255, 170)
c.ellipse(
cx, cy + 20,
135,
30 + math.cos(ringAngle) * 8
)
for (i <- 0 until 6) {
val a = ringAngle + i * math.Pi / 3.0
val px = cx + math.cos(a) * 83
val py = cy + 20 + math.sin(a) * 18
c.noStroke()
c.fill(0, 240, 255, 230)
c.ellipse(px, py, 7, 7)
c.fill(0, 200, 255, 55)
c.ellipse(px, py, 15, 15)
}
}
// ----------------------------------------------------------
// WALKING LEGS
// ----------------------------------------------------------
def drawLegs(c: CanvasDraw): Unit = {
val bx = robotX
val by = robotY + bob
var leftStep = 0.0
var rightStep = 0.0
if (walking) {
leftStep = math.sin(walkPhase) * 12
rightStep = math.sin(walkPhase + math.Pi) * 12
}
c.stroke(120, 230, 255)
c.strokeWeight(12)
c.line(
bx - 23, by - 35,
bx - 30 + leftStep, by - 65
)
c.line(
bx + 23, by - 35,
bx + 30 + rightStep, by - 65
)
c.stroke(35, 130, 190)
c.strokeWeight(5)
c.line(
bx - 30 + leftStep, by - 65,
bx - 42 + leftStep, by - 75
)
c.line(
bx + 30 + rightStep, by - 65,
bx + 42 + rightStep, by - 75
)
c.noStroke()
c.fill(0, 220, 255, 180)
c.ellipse(bx - 42 + leftStep, by - 76, 20, 8)
c.ellipse(bx + 42 + rightStep, by - 76, 20, 8)
c.fill(30, 70, 125)
c.stroke(0, 230, 255)
c.strokeWeight(2)
c.ellipse(bx - 27 + leftStep, by - 49, 15, 15)
c.ellipse(bx + 27 + rightStep, by - 49, 15, 15)
}
// ----------------------------------------------------------
// ROBOT BODY
// ----------------------------------------------------------
def drawBody(c: CanvasDraw): Unit = {
val bx = robotX
val by = robotY + bob
c.fill(20, 85, 145)
c.stroke(0, 225, 255)
c.strokeWeight(2)
c.ellipse(bx - 39, by + 28, 27, 30)
c.ellipse(bx + 39, by + 28, 27, 30)
c.fill(15, 35, 72)
c.stroke(40, 210, 255)
c.strokeWeight(3)
c.ellipse(bx, by + 5, 78, 88)
c.fill(20, 70, 115)
c.stroke(80, 230, 255)
c.ellipse(bx, by + 12, 53, 59)
val glow = alpha(140 + math.sin(coreGlow) * 90)
c.noStroke()
c.fill(0, 200, 255, 45)
c.ellipse(bx, by + 12, 53, 53)
c.fill(0, 230, 255, glow)
c.ellipse(bx, by + 12, 30, 30)
c.fill(210, 255, 255, 240)
c.ellipse(bx, by + 12, 13, 13)
c.stroke(0, 230, 255, 200)
c.strokeWeight(2)
c.line(bx - 20, by + 28, bx - 10, by + 20)
c.line(bx + 20, by + 28, bx + 10, by + 20)
c.line(bx - 19, by - 8, bx - 10, by - 3)
c.line(bx + 19, by - 8, bx + 10, by - 3)
c.noStroke()
c.fill(110, 255, 255)
for (i <- 0 until 3) {
c.ellipse(bx - 9 + i * 9, by - 20, 4, 4)
}
}
// ----------------------------------------------------------
// ARMS AND WAVING
// ----------------------------------------------------------
def drawArms(c: CanvasDraw): Unit = {
val bx = robotX
val by = robotY + bob
val swing =
if (waving) math.sin(waveAngle) * 28
else math.sin(t * 2.0) * 5
c.stroke(65, 190, 240)
c.strokeWeight(11)
c.line(bx - 38, by + 28, bx - 56, by + 3)
c.line(bx - 56, by + 3, bx - 65, by - 17)
c.fill(20, 90, 155)
c.stroke(0, 225, 255)
c.strokeWeight(2)
c.ellipse(bx - 56, by + 3, 17, 17)
c.ellipse(bx - 65, by - 17, 18, 18)
val handX = bx + 70
val handY = by + 7 + swing
c.stroke(65, 190, 240)
c.strokeWeight(11)
c.line(bx + 38, by + 28, bx + 56, by + 4)
c.line(bx + 56, by + 4, handX, handY)
c.fill(20, 90, 155)
c.stroke(0, 225, 255)
c.strokeWeight(2)
c.ellipse(bx + 56, by + 4, 17, 17)
c.fill(100, 245, 255, 220)
c.ellipse(handX, handY, 21, 21)
c.stroke(120, 255, 255)
c.strokeWeight(2)
for (i <- 0 until 3) {
c.line(
handX + 3, handY + 2,
handX + 13, handY + 5 + i * 5
)
}
}
// ----------------------------------------------------------
// ROBOT HEAD AND FACE
// ----------------------------------------------------------
def drawHead(c: CanvasDraw): Unit = {
val hx = robotX
val hy = robotY + bob + 65
c.fill(20, 80, 130)
c.stroke(0, 220, 255)
c.strokeWeight(2)
c.rect(hx - 12, hy - 34, 24, 15)
c.noStroke()
c.fill(0, 150, 255, 35)
c.ellipse(hx, hy, 94, 76)
c.fill(15, 35, 68)
c.stroke(70, 220, 255)
c.strokeWeight(3)
c.rect(hx - 39, hy - 29, 78, 58)
c.fill(5, 20, 42)
c.stroke(0, 130, 210)
c.rect(hx - 31, hy - 21, 62, 41)
val eye = alpha(170 + math.sin(eyeGlow) * 75)
c.noStroke()
c.fill(0, 220, 255, 65)
c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 23, 23)
c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 23, 23)
c.fill(80, 255, 255, eye)
c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 11, 11)
c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 11, 11)
c.fill(255, 255, 255, 240)
c.ellipse(hx - 15 + lookX, hy + 5 + lookY, 4, 4)
c.ellipse(hx + 15 + lookX, hy + 5 + lookY, 4, 4)
c.stroke(0, 220, 255)
c.strokeWeight(2)
c.line(hx - 10, hy - 10, hx + 10, hy - 10)
c.stroke(80, 210, 255)
c.strokeWeight(3)
c.line(hx, hy + 29, hx, hy + 43)
c.noStroke()
c.fill(0, 240, 255, eye)
c.ellipse(hx, hy + 47, 10, 10)
c.fill(0, 230, 255, 45)
c.ellipse(hx, hy + 47, 21, 21)
c.fill(20, 100, 155)
c.stroke(0, 220, 255)
c.strokeWeight(2)
c.rect(hx - 46, hy - 12, 8, 24)
c.rect(hx + 38, hy - 12, 8, 24)
c.noStroke()
c.fill(120, 255, 255)
c.ellipse(hx - 42, hy, 4, 10)
c.ellipse(hx + 42, hy, 4, 10)
// HI SPEECH BUBBLE
if (hiTimer > 0) {
c.fill(0, 30, 65, 230)
c.stroke(0, 240, 255)
c.strokeWeight(2)
c.rect(hx + 42, hy + 28, 66, 34)
c.noStroke()
c.fill(120, 255, 255)
c.text("HI!", hx + 59, hy + 40)
}
}
// ----------------------------------------------------------
// HUD
// ----------------------------------------------------------
def drawHUD(c: CanvasDraw): Unit = {
val x = 30.0
val y = cheight - 55.0
c.noStroke()
c.fill(0, 220, 255)
c.text("HOLOGRAM SYSTEM", x, y)
c.text("ROBOT STATUS: ONLINE", x, y - 22)
c.fill(100, 220, 255, 160)
for (i <- 0 until 12) {
val h = 5 + ((i * 7 + t.toInt) % 18)
c.rect(x + i * 12, y - 48, 6, h)
}
c.fill(0, 220, 255, 220)
c.text("CLICK ANYWHERE TO MOVE AND SAY HI", 20, 25)
}
// ----------------------------------------------------------
// CLICK EFFECT
// ----------------------------------------------------------
def drawClickEffect(c: CanvasDraw): Unit = {
if (clicked) {
val radius = 15 + (t * 18 % 55)
c.noFill()
c.stroke(0, 230, 255, 180)
c.strokeWeight(2)
c.ellipse(clickX, clickY, radius * 2, radius * 2)
c.stroke(120, 80, 255, 130)
c.ellipse(
clickX, clickY,
radius * 1.4,
radius * 1.4
)
if (radius > 65) {
clicked = false
}
}
}
// ----------------------------------------------------------
// SETUP
// ----------------------------------------------------------
def setup(c: CanvasDraw): Unit = {
c.background(5, 10, 28)
}
// ----------------------------------------------------------
// MAIN ANIMATION LOOP
// ----------------------------------------------------------
def drawLoop(c: CanvasDraw): Unit = {
t += 0.04
ringAngle += 0.045
eyeGlow += 0.08
coreGlow += 0.06
drawBackground(c)
// Click to choose destination
if (isMousePressed) {
targetX = clamp(
mouseX.toDouble,
60.0,
cwidth - 60.0
)
targetY = clamp(
mouseY.toDouble,
160.0,
280.0
)
clickX = mouseX.toDouble
clickY = mouseY.toDouble
clicked = true
waving = true
waveAngle = 0.0
hiTimer = 100
}
// Move towards the target
val distanceX = targetX - robotX
walking = math.abs(distanceX) > 2.0
if (walking) {
robotX += clamp(distanceX, -2.0, 2.0)
walkPhase += 0.25
}
// Smooth vertical movement
val distanceY = targetY - robotY
robotY += clamp(distanceY, -1.0, 1.0)
// Floating animation
bob = math.sin(t * 2.0) * 3.0
// Eyes follow the target
lookX = clamp(
(targetX - robotX) * 0.035,
-5.0,
5.0
)
lookY = clamp(
(targetY - robotY) * 0.025,
-4.0,
4.0
)
// Wave animation
if (waving) {
waveAngle += 0.22
if (waveAngle > 12.0) {
waving = false
waveAngle = 0.0
}
}
if (hiTimer > 0) {
hiTimer -= 1
}
// Draw everything
drawPlatform(c)
drawRings(c)
drawLegs(c)
drawBody(c)
drawArms(c)
drawHead(c)
drawClickEffect(c)
drawHUD(c)
}
}
// ============================================================
// START ROBOT
// ============================================================
val sketch = new Sketch
canvasSketch(sketch)