On Click — Event Handling

So far your scenes have run the same way every time — code runs top to bottom and that’s it. But real programs respond to what the user does.

An event is something that happens — a mouse click, a key press, a timer firing. You write a function that should run when that event occurs, and you connect it to the event. That connected function is called an event handler.

In scene3d, every mesh supports on_click(handler) — pass it a function and that function runs every time the user clicks the mesh.

Open In Jupyter K-12

A Simple Click Handler

The simplest way to handle a click is with a lambda — a tiny one-line function with no name.

sphere.on_click(lambda: sphere.set_color('#e94560'))

Read this as: “When the sphere is clicked, run this: sphere.set_color('#e94560')

Run the cell, then click the sphere in the scene to change its color.

import scene3d

scene = scene3d.Scene()
scene.set_sky('#1a1a2e')
scene.set_ground(length=10, width=10)

sphere = scene3d.Shapes.Sphere(diameter=1.5, segments=16)
sphere.set_color('#4488ff')
sphere.set_position(0, 1, 0)
sphere.on_click(lambda: sphere.set_color('#e94560'))
scene.add(sphere)

scene.run()

How on_click Works

sphere.on_click(lambda: sphere.set_color('#e94560'))
Part What it does
sphere.on_click(...) Registers a handler for click events on this mesh
lambda: Creates a small anonymous function with no name
sphere.set_color(...) The code that runs when the click happens

The handler must be registered before the mesh is added to the scene with scene.add(). The handler is registered when you call on_click, but it doesn’t run until a click occurs. You can think of it as leaving a note: “If this is ever clicked, do this.”

Lambda is great for simple single-line actions. For anything more complex, use a named function.

{ “question_type”: “multiple_choice”, “question”: “When does the function passed to on_click actually run?”, “options”: [ { “key”: “a”, “text”: “Immediately when on_click is called” }, { “key”: “b”, “text”: “When scene.run() is called” }, { “key”: “c”, “text”: “Every frame, just like on_frame” }, { “key”: “d”, “text”: “When the user clicks the mesh” } ], “answer”: “d”, “submitted_answer”: “” }

Named Functions for More Complex Handlers

A lambda can only hold one expression. When the handler needs multiple steps — like updating a counter, choosing a new color, and updating a HUD — use a regular named function instead.

The scene below counts how many times the sphere has been clicked and cycles through colors. Click the sphere several times to watch it change!

import scene3d

scene = scene3d.Scene()
scene.set_sky('#0f3460')
scene.set_ground(length=10, width=10)

ctx = scene.get_context('2d')
colors = ['#4488ff', '#e94560', '#f5a623', '#44cc88', '#cc44ff']
click_count = 0

def on_sphere_click():
  global click_count
  click_count += 1
  sphere.set_color(colors[click_count % len(colors)])
  ctx.clear()
  ctx.fill_style = '#ffffff'
  ctx.font = '22px sans-serif'
  ctx.fill_text(f'Clicks: {click_count}', 10, 30)

sphere = scene3d.Shapes.Sphere(diameter=1.5, segments=16)
sphere.set_color('#4488ff')
sphere.set_position(0, 1, 0)
sphere.on_click(on_sphere_click)
scene.add(sphere)

ctx.fill_style = '#ffffff'
ctx.font = '22px sans-serif'
ctx.fill_text('Click the sphere!', 10, 30)

scene.run()

{ “question_type”: “true_false”, “question”: “A lambda is the only way to pass a handler to on_click — named functions cannot be used.”, “answer”: “False”, “submitted_answer”: “” }

Multiple Objects, Multiple Handlers

Every mesh can have its own click handler — they don’t interfere with each other. The scene below has three shapes. Each one responds to clicks independently. Try clicking each shape and reading the message on screen.

import scene3d

scene = scene3d.Scene()
scene.set_sky('#0f3460')
scene.set_ground(length=14, width=10)

ctx = scene.get_context('2d')
message = 'Click any shape!'

def show(text):
  global message
  message = text
  ctx.clear()
  ctx.fill_style = '#ffffff'
  ctx.font = '22px sans-serif'
  ctx.fill_text(message, 10, 30)

box = scene3d.Shapes.Box(width=1.2, height=1.2, depth=1.2)
box.set_color('#e94560')
box.set_position(-4, 0.6, 0)
box.on_click(lambda: show('You clicked the box!'))
scene.add(box)

sphere = scene3d.Shapes.Sphere(diameter=1.2, segments=16)
sphere.set_color('#f5a623')
sphere.set_position(0, 0.6, 0)
sphere.on_click(lambda: show('You clicked the sphere!'))
scene.add(sphere)

cylinder = scene3d.Shapes.Cylinder(diameter=1.0, height=1.5, tessellation=16)
cylinder.set_color('#44cc88')
cylinder.set_position(4, 0.75, 0)
cylinder.on_click(lambda: show('You clicked the cylinder!'))
scene.add(cylinder)

show('Click any shape!')
scene.run()

{ “question_type”: “multiple_choice”, “question”: “If you call on_click on three different meshes, what happens when the user clicks one of them?”, “options”: [ { “key”: “a”, “text”: “All three handlers run” }, { “key”: “b”, “text”: “Only the handler for the clicked mesh runs” }, { “key”: “c”, “text”: “The first handler registered always runs” }, { “key”: “d”, “text”: “on_click only works for one mesh at a time” } ], “answer”: “b”, “submitted_answer”: “” }

{ “question_type”: “freeform”, “question”: “In one sentence, describe what an ‘event handler’ is.”, “answer”: “a function that runs when an event occurs”, “submitted_answer”: “” }

Try It Yourself

The scene below has a row of five spheres. Each one tracks how many times it’s been clicked and changes to a random new color with every click. Click them as fast as you can!

import scene3d
import random

scene = scene3d.Scene()
scene.set_sky('#0f3460')
scene.set_ground(length=16, width=8)

palette = ['#e94560', '#f5a623', '#4488ff', '#44cc88', '#cc44ff', '#ff8844', '#44ffcc']
spheres = []
clicks = [0, 0, 0, 0, 0]
ctx = scene.get_context('2d')

def update_hud():
  ctx.clear()
  ctx.fill_style = '#ffffff'
  ctx.font = '20px sans-serif'
  ctx.fill_text('Clicks: ' + '  |  '.join(str(c) for c in clicks), 10, 28)

def make_handler(idx):
  def handler():
    clicks[idx] += 1
    spheres[idx].set_color(random.choice(palette))
    update_hud()
  return handler

for i in range(5):
  s = scene3d.Shapes.Sphere(diameter=1.2, segments=16)
  s.set_color(palette[i])
  s.set_position(i * 3 - 6, 0.7, 0)
  s.on_click(make_handler(i))
  scene.add(s)
  spheres.append(s)

update_hud()
scene.run()

Think about an interactive game or experience you’d like to build — a puzzle, a quiz, an instrument you play with clicks.

Which objects would be clickable? What would each click do? Would clicks change just one object, or would clicking one thing affect others too? Describe the experience you have in mind.