Scene es el punto de entrada de toda animación: crea los objetos, programa la línea de tiempo, organiza los segmentos y entrega el resultado al ejecutable de Gaanim. Cada script crea una escena, la llena y termina con scene.render().

from gaanim import BLUE, Scene

scene = Scene(frame=(16, 9), background="#0f172a")
dot = scene.geometry.circle(0.6).fill(BLUE)
scene.play([dot.animate.create().duration(0.8)])
scene.wait(0.5)
scene.render()

Esta página documenta la escena, su lienzo (scene.canvas), su cámara (scene.camera) y las secciones. Los métodos comunes de los objetos están en Drawable, las animaciones en Animaciones y los temas en Temas y colores. Para ejecutar, validar y exportar un script, consulta la línea de comandos.

Constructor

CLASESceneScene(*, frame: tuple[float, float] = (16.0, 9.0), background: BackgroundLike | None = None, margin: float | None = None, theme: ThemeName | Theme | None = "technical", post: PostProcess | None = None)
Los píxeles de salida no se eligen aquí sino al previsualizar o exportar, así que la composición es la misma a cualquier resolución. Un marco no finito o no positivo, WGSL inválido o un tema desconocido lanzan ValueError; un theme de otro tipo lanza TypeError. La antigua forma Scene(width, height) en píxeles ya no se acepta.
Parámetros
NombreTipoPredeterminadoDescripción
frametuple[float, float](16.0, 9.0)Ancho y alto del marco lógico, centrado en el origen. Toda la geometría, los márgenes, los tamaños de texto y los trazos usan esta unidad.
backgroundBackgroundLike | NoneNoneColor, Brush o Background. Tiene prioridad sobre el fondo del tema.
marginfloat | NoneNoneMargen uniforme en unidades lógicas; reduce el área segura.
themeThemeName | Theme | None"technical"Nombre o alias de un tema incluido, o un Theme. None crea la escena sin tema.
postPostProcess | NoneNonePostprocesado WGSL aplicado a todos los segmentos que no lo sustituyan.
Devuelve: Scene — Una escena vacía, lista para crear objetos.
Ejemplo
from gaanim import Scene

scene = Scene(frame=(16, 9), background="#0f172a", margin=0.5, theme="presentation")
print(scene.canvas.frame_width, scene.canvas.safe_width)  # 16.0 15.0
16.0 15.0

Sin theme, la escena usa el tema technical: fondo gris casi negro (#121212), texto y ejes claros y formas rellenas con el color de acento. Un background explícito gana al fondo del tema, y los colores de cada objeto ganan a los del tema. theme=None deja un lienzo sin tema: fondo blanco (si no pasas background) y objetos sin estilo también blancos, así que tendrás que darles color. Consulta Canvas.set_theme.

Capacidades

Las fábricas viven en handles que pertenecen a la escena. Un objeto de una escena no puede usarse en otra: pasarlo lanza ValueError.

PROPIEDADScene.geometry / text / layout / media / viz / slides / mechanics / sections / assetsscene.geometry -> Geometry · scene.text -> Typography · scene.layout -> LayoutBuilder · scene.media -> MediaLibrary · scene.viz -> Visualization · scene.slides -> SlideKit · scene.mechanics -> Mechanics · scene.sections -> SceneSections · scene.assets -> AssetManager
Cada propiedad agrupa las fábricas de un área.
Devuelve: handle — Un handle de solo lectura ligado a esta escena.
Ejemplo
PropiedadContenido
scene.geometryPrimitivas, trayectorias, flechas, geometría reactiva y 3D. Ver Geometría.
scene.textTexto, ecuaciones, Typst y código. Ver Texto.
scene.layoutCajas, grids, estilos y zonas. Ver Layout.
scene.mediaImágenes, SVG, vídeo, audio y Lottie. Ver Medios y Audio.
scene.vizEjes, funciones, gráficas, parámetros y matrices. Ver Visualización.
scene.slidesTarjetas, viñetas, tablas e identidad de presentación. Ver Diapositivas.
scene.mechanicsMuelles, cotas, fuerzas y apoyos. Ver Mecánica.
scene.sectionsAgenda y barra de progreso de secciones. Ver Secciones.
scene.assetsCarpeta de recursos, precarga y recarga. Ver Recursos.
shape = scene.geometry.circle(1)
title = scene.text("Resultado", role="title")
page = scene.layout.column([title, shape])
value = scene.viz.parameter(0.0)
PROPIEDADScene.canvascanvas: Canvas
Marco, área segura, márgenes, fondo, tema, fuentes y postprocesado. Los miembros de marco y área segura están abajo; los de estilo, en Temas y colores.
Devuelve: Canvas — El lienzo lógico de la escena.
PROPIEDADScene.cameracamera: Camera
Consulta Cámara.
Devuelve: Camera — La cámara que se ve en la previsualización y en la exportación.
PROPIEDADScene.timetime: TimeInput
Es la misma fuente que scene.viz.time. Pásala a computed o a un setter absoluto; sigue los seeks exactos y la exportación.
Devuelve: TimeInput — Los segundos absolutos de la línea de tiempo como entrada reactiva.
Ejemplo
dot.move_to(computed(lambda t: -4 + t, inputs=[scene.time]), 0)
scene.wait(3)

Las narraciones (Scene.voiceover, Scene.narration_script y Scene.live_take) están en Audio.

Marco lógico y área segura

El marco es el rectángulo que se ve, en unidades lógicas y centrado en el origen: con el predeterminado de 16 × 9, x va de -8 a 8 e y de -4.5 a 4.5. El área segura es el marco menos los márgenes; la usan el layout (within="safe") y las colocaciones en bordes y esquinas.

PROPIEDADCanvas.frame_widthframe_width: float
Devuelve: float — Ancho del marco lógico; 16.0 en una escena predeterminada.
PROPIEDADCanvas.frame_heightframe_height: float
Devuelve: float — Alto del marco lógico; 9.0 en una escena predeterminada.
PROPIEDADCanvas.aspect_ratioaspect_ratio: float
Devuelve: float — Ancho dividido entre alto del marco.
PROPIEDADCanvas.safe_widthsafe_width: float
Devuelve: float — Ancho del marco menos los márgenes izquierdo y derecho.
PROPIEDADCanvas.safe_heightsafe_height: float
Devuelve: float — Alto del marco menos los márgenes superior e inferior.
MÉTODOCanvas.set_marginset_margin(margin: float) -> None
Equivale a Scene(margin=...) después de crear la escena.
Parámetros
NombreTipoPredeterminadoDescripción
marginfloatobligatorioMargen igual en los cuatro lados, en unidades lógicas.
Devuelve: None — Reemplaza el área segura actual.
Ejemplo
scene.canvas.set_margin(0.4)
print(scene.canvas.safe_width, scene.canvas.safe_height)  # 15.2 8.2
15.2 8.2
MÉTODOCanvas.set_safe_areaset_safe_area(*, top: float = 0.0, right: float = 0.0, bottom: float = 0.0, left: float = 0.0) -> None
Útil cuando una marca o una plataforma reserva franjas propias, como la interfaz de un vídeo vertical.
Parámetros
NombreTipoPredeterminadoDescripción
topfloat0.0Margen superior en unidades lógicas.
rightfloat0.0Margen derecho.
bottomfloat0.0Margen inferior.
leftfloat0.0Margen izquierdo.
Devuelve: None — Reemplaza el área segura actual.
Ejemplo
scene.canvas.set_safe_area(top=0.5, bottom=1.0)
print(scene.canvas.safe_height)  # 7.5
7.5
MÉTODOCanvas.set_presetset_preset(name: Literal["widescreen", "vertical", "square"]) -> None
"widescreen" da un marco 16 × 9 con área segura 15 × 8, "vertical" un marco 9 × 16 con área segura 8 × 13.5 y "square" un marco 10 × 10 con área segura 9 × 9.
Parámetros
NombreTipoPredeterminadoDescripción
name"widescreen" | "vertical" | "square"obligatorioComposición estándar.
Devuelve: None — Reemplaza el marco lógico y el área segura.
Ejemplo
scene.canvas.set_preset("vertical")
page = scene.layout.column([scene.text("Vídeo vertical", role="title")], within="safe")

Línea de tiempo

Cada llamada a play o wait avanza el cursor de autoría. Las animaciones de una misma lista empiezan juntas; llamadas sucesivas van una detrás de otra. scene.launch(...) (o play(..., advance=False)) empieza en el cursor sin moverlo, para una animación larga que sigue mientras programas lo demás.

MÉTODOScene.playplay(items: Playable | Sequence[Playable], *, duration: Optional[float] = None, easing: Optional[Easing] = None, advance: bool = True) -> None
Programa el bloque de forma atómica: un Anim ya usado, de otra escena, repetido o que escribe un canal ocupado en el mismo tramo lanza ValueError sin aplicar ningún cambio. duration y easing son valores por defecto; los que fija cada Anim o cada grupo tienen prioridad.
Parámetros
NombreTipoPredeterminadoDescripción
itemsPlayable | Sequence[Playable]obligatorioUn Anim, audio, vídeo, Lottie o Composition, o una lista que se reproduce en paralelo.
durationfloat | NoneNoneDuración para las animaciones que no fijan la suya.
easingEasing | NoneNoneEasing para las animaciones que no fijan el suyo.
advanceboolTrueCon False el cursor no se mueve: el bloque empieza y sigue sonando bajo lo que programes después. Es lo mismo que scene.launch.
Devuelve: None — Avanza el cursor hasta el final del bloque, salvo con advance=False.
Ejemplo
from gaanim import BLUE, GOLD, Easing, Scene

scene = Scene(frame=(16, 9), theme="technical")
circle = scene.geometry.circle(0.8).fill(BLUE).move_to(-1.6, 0)
rect = scene.geometry.rect(1.8, 1.0).fill(GOLD).move_to(1.6, 0)
scene.play([circle.animate.create(), rect.animate.grow_from_center()], duration=0.8)
scene.play(circle.animate.shift_by(1.2, 0), easing=Easing.SNAPPY)
scene.render()
MÉTODOScene.launchlaunch(items: Playable | Sequence[Playable], *, duration: Optional[float] = None, easing: Optional[Easing] = None) -> None
Empieza el bloque en el cursor y deja el cursor donde estaba: lo que programes después (esperas, otros play, cortes, segmentos) ocurre mientras el bloque sigue. Sirve para un giro que dura varios cortes o una música de fondo. Animar el mismo canal del mismo objeto antes de que termine lanza ValueError. El vídeo acaba en el cursor, así que lo que siga corriendo ahí se corta; gaanim check lo avisa. Los objetos de un segmento salen de pantalla al terminar este, salvo que scene.persist los mantenga.
Parámetros
NombreTipoPredeterminadoDescripción
itemsPlayable | Sequence[Playable]obligatorioLo mismo que acepta play.
duration / easingfloat | Easing | NoneNoneValores por defecto, como en play.
Devuelve: None — No mueve el cursor.
Ejemplo
import math
rueda = scene.geometry.regular_polygon(6, 1.5).fill("#38bdf8")
puntos = [scene.geometry.dot(0.2).move_to(x, -3) for x in (-4, -2, 0, 2, 4)]
scene.launch(rueda.animate.rotate_by(math.tau).duration(5.0))
for punto in puntos:
    scene.play(punto.animate.fade_in().duration(1.0))
MÉTODOScene.tempotempo(bpm: float, offset: float = 0.0, *, beats_per_bar: int = 4) -> None
Rejilla de tempo para cortar a tiempo con la música: scene.beats(n) convierte pulsos en segundos y scene.wait_until(beat=n) o scene.wait_until(bar=n) llevan el cursor a un pulso o al inicio de un compás. Si el cursor ya pasó ese pulso, wait_until lanza ValueError y dice cuánto se alargó el plano anterior, en vez de desfasarse en silencio. La barra de reproducción del editor dibuja una línea en cada compás. Valores no válidos lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
bpmfloatobligatorioPulsos por minuto; positivo.
offsetfloat0.0Segundo de la línea de tiempo donde cae el pulso 0, el primer tiempo de la música.
beats_per_barint4Pulsos por compás.
Devuelve: None — Fija el tempo de la escena.
Ejemplo
scene.tempo(128, offset=0.0)
logo = scene.geometry.star(5, 1.2, 0.5).fill("#fbbf24")
scene.play(logo.animate.grow_from_center().duration(scene.beats(2)))
scene.wait_until(bar=1)
scene.play(logo.animate.rotate_by(1.0).duration(scene.beats(4)))
scene.wait_until(bar=2)
MÉTODOScene.waitwait(seconds: float) -> None
Parámetros
NombreTipoPredeterminadoDescripción
secondsfloatobligatorioTiempo que se mantiene la imagen, en segundos.
Devuelve: None — Avanza el cursor.
Ejemplo
scene.wait(1.0)
MÉTODOScene.fade_out_allfade_out_all(seconds: float) -> None
Desvanece a la vez, con easing suave, todos los objetos del segmento activo. Es la forma rápida de cerrar una escena.
Parámetros
NombreTipoPredeterminadoDescripción
secondsfloatobligatorioDuración del fundido.
Devuelve: None — Avanza el cursor seconds.
Ejemplo
from gaanim import Scene

scene = Scene(frame=(16, 9), theme="technical")
title = scene.text("Fin", role="title")
scene.play(title.animate.write())
scene.fade_out_all(0.6)
scene.render()
PROPIEDADScene.cursorcursor: float
Cuenta a través de todos los segmentos y es de solo lectura.
Devuelve: float — Posición del cursor de autoría, en segundos absolutos de la línea de tiempo.
Ejemplo
scene.play([title.animate.write().duration(0.8)])
reveal_time = scene.cursor  # 0.8
MÉTODOScene.markermarker(name: str) -> None
Da nombre al instante actual de la línea de tiempo global. Es solo un metadato: no pausa la reproducción. El editor lo dibuja como una banderita violeta sobre la barra de tiempo (al pasar el cursor muestra el nombre y un clic salta a él), y gaanim export --from / --to lo aceptan en lugar de segundos. Un nombre vacío, repetido o que se lee como número lanza ValueError. Para instantes dentro de una Composition, usa label.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre único del instante; se recortan los espacios.
Devuelve: None — No añade duración ni mueve el cursor.
Ejemplo
scene.play(intro)
scene.marker("climax")
scene.play(outro)
scene.marker("fin")
gaanim export escena.py --output climax.mp4 --from climax --to fin
PROPIEDADScene.markersmarkers: list[SceneMarker]
Cada SceneMarker tiene name, time (segundos absolutos) y segment (el segmento activo al crearlo).
Devuelve: list[SceneMarker] — Los marcadores creados hasta ahora, en orden temporal.
Ejemplo
scene.wait(1.5)
scene.marker("climax")
print([(m.name, m.time) for m in scene.markers])  # [('climax', 1.5)]
[('climax', 1.5)]

Segmentos

Un segmento es la unidad estructural de la escena: agrupa objetos, define la transición de entrada, las notas para Presenter View, la plantilla y un fondo propio. Los límites entre segmentos son continuos; stop() marca dónde la reproducción interactiva espera al presentador.

MÉTODOScene.segmentsegment(name: str, transition: Transition | None = None, *, notes: str | None = None, template: Callable[..., Layout] | None = None, background: BackgroundLike | None = None, post: PostProcess | Literal[False] | None = None, target: float | None = None) -> Segment
Crea el segmento y lo activa. La primera llamada sustituye al segmento implícito inicial, que aún no tiene contenido. Un nombre vacío o repetido, una transición en el primer segmento, un target que no sea un número positivo de segundos o un guion ilegible lanzan ValueError; un post de otro tipo lanza TypeError.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre no vacío, único en la escena sin distinguir mayúsculas.
transitionTransition | NoneNoneTransición de entrada desde el segmento anterior. Ver Transiciones.
notesstr | NoneNoneNotas del orador que muestra Presenter View; una línea [stop] marca dónde avanzas. La sección ## nombre del guion, si existe, las sustituye (ver El guion de la charla).
templateCallable[..., Layout] | NoneNonePlantilla de Python que se rellena después con Segment.bind.
backgroundBackgroundLike | NoneNoneFondo solo mientras el segmento está activo; None usa el de la escena.
postPostProcess | False | NoneNonePostprocesado del segmento: otro PostProcess, False para ninguno o None para heredar scene.canvas.post.
targetfloat | NoneNoneSegundos que prevés dedicarle al presentar. Presenter View compara con él el tiempo que pasas en la diapositiva (ver El tiempo de la charla). La de la charla entera se fija con scene.slides.target.
Devuelve: Segment — Handle que aceptan link y bind.
Ejemplo
intro = scene.segment("Introducción", notes="Presenta el objetivo.", template=title_slide)
intro.bind(title=scene.text("Una idea clara", role="title"))
scene.wait(0.5)
details = scene.segment(
    "Detalles",
    Transition.cross_fade(0.4),
    background=Brush.linear(["#172554", "#0f172a"], start=(-8, 0), end=(8, 0)),
)
scene.wait(0.5)
MÉTODOSegment.bindbind(**slots: Any) -> Box
Rellena la plantilla del segmento. Un hueco obligatorio que falta o uno que no existe lanzan TypeError; un segmento sin plantilla lanza ValueError. Las plantillas incluidas (title_slide, lecture, comparison…) se importan desde gaanim.
Parámetros
NombreTipoPredeterminadoDescripción
slots**Any{}Un argumento con nombre por hueco de la plantilla.
Devuelve: Layout — El layout raíz del segmento.
Ejemplo
results = scene.segment("Resultados", template=comparison, notes="Compara los dos modelos.")
results.bind(title=scene.text("Resultados", role="title"), left=baseline, right=proposed)
scene.stop("comparación")
MÉTODOSegment.motion_blursegment.motion_blur(shutter_angle=180.0, samples=8, *, phase=None) -> Segment
Desenfoque de movimiento de este segmento en las exportaciones y las capturas, en lugar del de la escena. Los segmentos sin uno propio usan el de la escena, así que cada segmento puede comparar un ángulo de obturador o una phase. Un cuadro justo en un corte pertenece al segmento que entra. Un valor fuera de rango lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
shutter_angle, samples, phasefloat | None, int, float | None180.0, 8, NoneComo en scene.canvas.motion_blur None deja nítido el segmento.
Devuelve: Segment — El mismo segmento.
Ejemplo
scene.canvas.motion_blur(180)
scene.segment("normal")
scene.wait(1)
scene.segment("amplio").motion_blur(360, samples=16)
scene.wait(1)
scene.segment("nítido").motion_blur(None)
scene.wait(1)
MÉTODOScene.linklink(from_: Segment, to: Segment, transition: Transition) -> None
Declara la transición entre dos segmentos ya creados. Hace falta para Transition.morph, cuyos pares incluyen objetos del segmento de destino. Un segmento de otra escena, o un to que no va después de from_, lanzan un error.
Parámetros
NombreTipoPredeterminadoDescripción
from_SegmentobligatorioSegmento de origen.
toSegmentobligatorioSegmento de destino, posterior a from_.
transitionTransitionobligatorioTransición que sustituye a la de entrada de to.
Devuelve: None — No mueve el cursor.
Ejemplo
overview = scene.segment("Resumen")
card = scene.geometry.rect(3, 2).fill(BLUE).move_to(-4, 0)
scene.wait(0.5)
detail = scene.segment("Detalle")
panel = scene.geometry.rect(12, 6).fill(BLUE)
scene.wait(0.5)
scene.link(overview, detail, Transition.morph(0.8, pairs=[(card, panel)]))
MÉTODOScene.stopstop(name: Optional[str] = None, *, loop: Optional[Playable | Sequence[Playable]] = None, until: Optional[Condition] = None) -> None
Pausa la reproducción interactiva cuando el cursor llega a este instante. En el límite de un segmento, el segmento saliente sigue visible hasta que se avanza, así que no hace falta un wait() final. Si la diapositiva todavía está entrando con su transición, la pausa espera a que termine y avanza el cursor hasta ahí, para que la presentación nunca descanse en una diapositiva a medio entrar. La exportación, las capturas y los seeks ignoran las pausas. Después de un live_take() grabado, la pausa espera tanto como la pausa real del orador (ver Audio). Con loop, la animación se coloca justo después de la pausa y, en lugar de congelar la imagen, se repite mientras el orador habla: el siguiente paso sale del bucle y sigue desde su final, y retroceder lo salta. Al exportar se reproduce una vez. Para que la repetición no salte, haz que el bucle termine como empieza. Un nombre vacío, una segunda pausa en el mismo instante del segmento o un bucle sin duración lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
namestr | NoneNoneEtiqueta de la pausa en Presenter View.
loopAnim | list | NoneNoneAnimación ambiental, con la misma forma que scene.play, que se repite mientras la presentación descansa en la pausa.
untilCondition | NoneNoneCondición del público con la que la pausa avanza sola al presentar. Ver Avanzar por sí sola.
Devuelve: None — Sin loop, no añade duración ni cambia la imagen, salvo lo que falte de la transición de entrada; con loop, avanza además el cursor lo que dura el bucle.
Ejemplo
scene.play([result.animate.write().duration(0.6)])
scene.stop("resultado")
scene.wait(0.5)
# Las flechas siguen moviéndose mientras se explica la pausa.
scene.stop("dos-placas", loop=sequence(
    arrow.animate.shift_by(0.5, 0).duration(0.75),
    arrow.animate.shift_by(-0.5, 0).duration(0.75),
))
MÉTODOScene.rehearsalrehearsal(players: int | Sequence[str] = 12, *, seed: int = 0, arrive: Optional[float] = None, skill: float | Sequence[float] = 0.6, speed: float = 0.5) -> None
Describe el público inventado que juega la escena fuera de una presentación en vivo. En la previsualización, la exportación y las capturas sus jugadores entran a la sala, responden cada encuesta y cuestionario, el reloj baja y la clasificación suma sus puntos; las zonas vivas también lo usan. Las respuestas de una pregunta llegan entre que se abre y la pausa donde la presentación las esperaría. Los mismos argumentos dan siempre el mismo ensayo. Sin llamarlo ensayan 12 jugadores. Sin jugadores o con más de 200, un apodo vacío o repetido, skill o speed fuera de 0–1 o un arrive negativo lanzan ValueError. Ver Ensayo.
Parámetros
NombreTipoPredeterminadoDescripción
playersint | Sequence[str]12Cuántos jugadores (Ana, Beto, Caro…) o sus apodos, en orden de llegada; de 1 a 200.
seedint0Elige otro grupo: otros tiempos, respuestas y personajes.
arrivefloat | NoneNoneSegundos en que entran todos; por defecto, entre scene.audience() y la primera pausa de la sala.
skillfloat | Sequence[float]0.6Qué parte de las preguntas aciertan en promedio, de 0 a 1; con equipos, puede ser un valor por equipo.
speedfloat0.5Qué tan pronto responden, de 0 (al final) a 1 (al instante).
Devuelve: None —
Ejemplo
scene.rehearsal(24, seed=3, skill=0.7)
audience = scene.audience()
MÉTODOScene.questionquestion(question: Question) -> Poll
Abre una pregunta escrita fuera de Python, con su tiempo, puntos, imagen, modo y ensayo. Ver Preguntas en un archivo.
Parámetros
NombreTipoPredeterminadoDescripción
questionQuestionobligatorioUna pregunta de gaanim.load_questions, que lee un Markdown o un CSV.
Devuelve: Poll — Un cuestionario si la pregunta tiene respuestas correctas; si no, una encuesta.
Ejemplo
>>>from gaanim import *
>>>scene = Scene(frame=(16, 9))
for q in load_questions("preguntas.md"):
    poll = scene.question(q)
MÉTODOScene.rosterroster(ask: str, *, required: bool = True) -> None
Pide a cada jugador un dato más al unirse, como su código de alumno o su nombre completo, para que el profesor sepa quién es quién. El dato nunca llega a la pantalla: solo aparece en los resultados que guarda la presentación y en gaanim relay results. Quien ya se había unido sigue jugando y lo da la próxima vez que se una. Un texto vacío o de más de 40 caracteres, o un segundo ask distinto en la misma escena, lanzan ValueError. Ver Guardar los resultados.
Parámetros
NombreTipoPredeterminadoDescripción
askstrobligatorioLo que el teléfono pide además del apodo, como "Código de alumno"; de 1 a 40 caracteres.
requiredboolTrueSi nadie puede unirse sin responderlo.
Devuelve: None —
Ejemplo
scene.roster("Código de alumno")
audience = scene.audience()
MÉTODOScene.teamsteams(names: Sequence[str], *, choose: bool = False, colors: Optional[Sequence[str]] = None) -> Teams
Hace que el juego se juegue por equipos. Los puntos de cada jugador suman para su equipo. Teams da names, colors, score(i), players(i) y average(i) (Parameter), leader() (el índice del equipo que va ganando) y bar(i, ...). En las zonas vivas cada jugador sabe p.team, p.team_index, p.team_count, p.team_score y p.team_rank. El ensayo reparte a sus jugadores igual. Menos de 2 o más de 6 equipos, un nombre vacío, largo o repetido, colores que no sean un #rrggbb por equipo, u otros equipos distintos en la misma escena lanzan ValueError. Ver Equipos.
Parámetros
NombreTipoPredeterminadoDescripción
namesSequence[str]obligatorioLos equipos, de 2 a 6, con nombres de hasta 20 caracteres.
chooseboolFalseSi el teléfono pregunta a qué equipo unirse; si no, el relay pone a cada jugador en el que tiene menos.
colorsSequence[str] | NoneNoneUn #rrggbb por equipo, como lo muestran los teléfonos; por defecto rosa, azul, naranja, verde, violeta y turquesa.
Devuelve: Teams — Los datos de los equipos; no dibuja nada.
Ejemplo
teams = scene.teams(["Rojo", "Azul"])
for team, x in [(0, -4), (1, 4)]:
    scene.viz.readout(teams.score(team), format=".0f").move_to(x, 0)
MÉTODOScene.pollpoll(question: str, options: Sequence[str], *, multiple: bool = False, image: Optional[str | os.PathLike[str]] = None, rehearse: Optional[Sequence[float]] = None) -> Poll
Abre una encuesta en el cursor. Recibe votos mientras una presentación está entre este punto y poll.close(), o el final del segmento. La escena decide cómo mostrarla con los datos de Poll: qr(tamaño) y bar(respuesta, ...) devuelven drawables; votes(i), share(i) y total() devuelven Parameter que siguen los votos en vivo; code y url son el código de la sesión y la dirección de votación. Al presentar un paquete .gaanim solo las barras siguen los votos en vivo; lo que pase por un computed muestra lo que se grabó. Fuera de una presentación vota el ensayo (scene.rehearsal). Ver Encuestas a la audiencia. Una pregunta o respuesta vacía, una respuesta repetida, menos de 2 o más de 6 respuestas, o pesos de rehearse que no sean uno por respuesta, negativos o todos cero lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
questionstrobligatorioPregunta que responde la audiencia.
optionsSequence[str]obligatorioEntre 2 y 6 respuestas distintas.
multipleboolFalseDeja elegir varias respuestas; total() y share(i) cuentan a los teléfonos que respondieron.
imagestr | PathLike | NoneNoneUna imagen, relativa al script, que los teléfonos muestran sobre la pregunta.
rehearseSequence[float] | NoneNoneUn peso por respuesta para el voto del ensayo ([1, 3] hace la segunda tres veces más popular); sin él vota al azar.
Devuelve: Poll — Los datos de la encuesta; no dibuja nada.
Ejemplo
poll = scene.poll("¿Qué crece más rápido?", ["x²", "2ˣ"], rehearse=[1, 2])
card = scene.geometry.rounded_rect(3.4, 3.4, 0.2).fill(WHITE).no_stroke().move_to(-4, 0)
qr = poll.qr(3.0).move_to(-4, 0)
bar = poll.bar(1, length=6).fill(GOLD).no_stroke().move_to(2, 0)
votes = scene.viz.readout(poll.votes(1), format=".0f").move_to(5.6, 0)
scene.stop()
MÉTODOScene.quizquiz(question: str, options: Sequence[str], correct: int, *, time: int = 20, points: int = 1000, image: Optional[str | os.PathLike[str]] = None, rehearse: Optional[float | Sequence[float]] = None) -> Poll
Abre un cuestionario al estilo Kahoot: una encuesta con respuesta correcta. Los teléfonos se unen al juego con un apodo, responden una sola vez antes de que se acabe el tiempo y una respuesta correcta gana points × (1 − tiempo / time / 2). quiz.reveal() marca dónde la presentación revela la respuesta en los teléfonos, quiz.remaining() es la cuenta atrás en segundos y scene.leaderboard da la clasificación. Una parte de rehearse fuera de 0–1 lanza ValueError. Ver Modo competencia.
Parámetros
NombreTipoPredeterminadoDescripción
questionstrobligatorioPregunta del juego.
optionsSequence[str]obligatorioEntre 2 y 6 respuestas distintas.
correctint | Sequence[int]obligatorioÍndice de la respuesta correcta (0 para la primera), o una lista para selección múltiple: acierta quien marca todas y ninguna otra.
timeint20Segundos para responder, de 5 a 300, medidos por el reloj del relay.
pointsint1000Puntos máximos de una respuesta correcta, de 100 a 10000.
imagestr | PathLike | NoneNoneUna imagen que los teléfonos muestran sobre la pregunta.
rehearsefloat | Sequence[float] | NoneNoneLa parte del ensayo que acierta, de 0 a 1 (0.3 para una pregunta difícil), o un peso por respuesta; sin él responden según su skill.
Devuelve: Poll — Los datos del cuestionario, como scene.poll.
Ejemplo
quiz = scene.quiz("¿Derivada de x²?", ["x", "2x", "x²/2"], correct=1, time=20, rehearse=0.6)
clock = scene.viz.readout(quiz.remaining(), format=".0f").move_to(-5, 3)
scene.wait(1)
scene.stop()
quiz.reveal()
scene.wait(1)
scene.stop()
MÉTODOScene.leaderboardleaderboard() -> Leaderboard
La clasificación del juego: los jugadores de los cuestionarios de la escena, de mejor a peor. Cada método recibe un puesto (0 para el primero): name(i, size=, weight=, font=, align=) devuelve el apodo como texto en vivo, points(i) y players() devuelven Parameter y bar(i, ...) una barra relativa al primero. Con ellos diseñas una lista, un podio o lo que quieras. Todo sigue a los resultados en vivo también al presentar un .gaanim, que guarda los glifos de los apodos; fuera de una presentación ordena a los jugadores del ensayo por los puntos de sus respuestas.
Devuelve: Leaderboard — Los datos de la clasificación; no dibuja nada.
Ejemplo
board = scene.leaderboard()
for rank in range(2):
    board.name(rank, size=0.5).move_to(-3, 1 - rank)
    scene.viz.readout(board.points(rank), format=".0f").move_to(3, 1 - rank)
MÉTODOScene.live_zonelive_zone(audience: Audience, behavior: Callable[[Player], Pose], *, bounds: tuple[float, float, float, float] = (-8.0, -4.5, 8.0, 4.5), size: float = 1.2, squash: float = 0.04, max_stretch: float = 1.4, lean: float = 0.05, max_lean: float = 0.35, follow: float = 1.0, look: float = 0.4, names: bool = False, name_size: float = 0.24, name_color: str = "#ffffff", name_gap: float = 0.08, name_weight: Optional[int] = 700, state: Optional[Mapping[str, float]] = None, update: Optional[Callable[[Player], State]] = None) -> LiveZone
Abre una zona viva en el cursor, hasta zone.close() o el final del segmento: al presentar, cada jugador llega como su personaje y el comportamiento lo coloca en cada cuadro; fuera de una presentación juega el ensayo. El comportamiento y la actualización se compilan, así que un .gaanim los ejecuta sin Python. Algo que el compilador no admite lanza gaanim.live.BehaviorError señalando la línea; unos límites vacíos, un tamaño no positivo, una expresión desconocida, state sin update (o al revés) o más de 8 números lanzan ValueError. Ver Zonas vivas.
Parámetros
NombreTipoPredeterminadoDescripción
audienceAudienceobligatorioEl público de scene.audience().
behaviorCallable[[Player], Pose]obligatorioUna función de Python que recibe al jugador p y devuelve pose(x, y, ...); se compila al llamar.
boundstuple[float, float, float, float](-8, -4.5, 8, 4.5)(x0, y0, x1, y1): los personajes se recortan a esta caja.
sizefloat1.2Alto de un personaje a escala 1.
squashfloat0.04Cuánto se estira en la dirección en que se mueve, por unidad de velocidad (hasta max_stretch, 1,4).
leanfloat0.05Cuánto se inclina al moverse de lado (hasta max_lean, 0,35 radianes).
followfloat1.0Cuánto se balancean orejas, sombreros y brotes detrás del movimiento.
lookfloat0.4Cuánto miran los ojos hacia donde va.
namesboolFalseDibuja el apodo bajo los pies (name_size, name_color, name_gap, name_weight).
stateMapping[str, float] | NoneNoneNúmeros que la zona guarda por jugador (hasta 8) y dónde empieza cada uno.
updateCallable[[Player], State] | NoneNoneCalcula cada STEP (1/60 s) los siguientes números de state y devuelve state(...).
Devuelve: LiveZone — La zona; close() la cierra en el cursor.
Ejemplo
from gaanim.live import pose
audience = scene.audience()

def en_fila(p):
    return pose(-6 + p.index * 1.2, -3, express="happy")

zone = scene.live_zone(audience, en_fila, names=True)
MÉTODOScene.charactercharacter(avatar: Optional[Sequence[int]] = None, *, name: str = "", size: float = 2.0) -> Character
Un personaje como los que crea el público en su teléfono, y que se mueve igual que allí. character.express("happy") hace una expresión desde el cursor; con loop=True la repite hasta la siguiente, y express() vuelve a su cara. Expresiones: happy, sad, hurt, winner y surprised. Como todo depende del tiempo de la línea de tiempo, saltar y exportar dan el mismo resultado.
Parámetros
NombreTipoPredeterminadoDescripción
avatarSequence[int] | NoneNoneLas partes: cuerpo, color, ojos, boca y extra. Sin ellas se deducen de name.
namestr""El apodo, que también marca el ritmo con que parpadea.
sizefloat2.0Alto en unidades, con sombreros y orejas.
Devuelve: Character — Un dibujable que respira, parpadea y hace expresiones.
Ejemplo
hero = scene.character([1, 0, 4, 2, 1], name="Ana").move_to(-3, 0)
scene.wait(1)
hero.express("winner", loop=True)
scene.wait(2)
MÉTODOScene.audienceaudience() -> Audience
El público del juego: los jugadores en el orden en que entraron, cada uno en un puesto (0 para el primero). name(i, size=, weight=, font=, align=) devuelve el apodo como texto en vivo; count(), joined(i) (1 si el puesto está ocupado, 0 si no) y age(i) (segundos desde que entró, hasta 60) devuelven Parameter; qr(size), url y code sirven para invitar. Con ellos diseñas una sala de espera, una arena o lo que quieras. Una escena que lo usa pide el apodo en cuanto el teléfono abre la página. Fuera de una presentación entran los jugadores del ensayo, uno tras otro. Ver Sala de espera.
Devuelve: Audience — Los datos del público; no dibuja nada.
Ejemplo
audience = scene.audience()
for slot in range(6):
    audience.name(slot, size=0.4).move_to((slot - 2.5) * 2, 0)
PROPIEDADScene.stopsstops: list[SceneStop]
Cada SceneStop tiene name (o None), time en segundos absolutos y segment. Léela al final del script, antes de render(), para pedir una captura por pausa; gaanim --diff --capture-stops hace lo mismo sin cambiar el script.
Devuelve: list[SceneStop] — Las pausas creadas hasta ahora, en orden temporal.
Ejemplo
if snapshots := os.environ.get("GAANIM_SNAPSHOTS"):
    scene.snapshots(snapshots, [stop.time for stop in scene.stops])

Al empezar un segmento, los objetos de los anteriores dejan de verse. Estos tres métodos cambian a qué segmento pertenece un objeto, en el cursor actual y sin alterar su estado visual. Un objeto de otra escena lanza ValueError.

MÉTODOScene.reusereuse(object: Drawable, *others: Drawable) -> None
Adopta objetos en el segmento activo. Un objeto visible en el segmento anterior queda quieto durante la transición automática y luego pasa a ser contenido del segmento activo. Llamarlo después de play() o wait() actúa en ese instante.
Parámetros
NombreTipoPredeterminadoDescripción
objectDrawableobligatorioPrimer objeto.
othersDrawable()Más objetos de la misma escena.
Devuelve: None
MÉTODOScene.z_layersz_layers(*names: str, default: Optional[str] = None) -> None
Nombra las capas de dibujo de la escena. Todo objeto de una capa (Drawable.z_layer) se dibuja encima de todos los de las capas de detrás, sea cual sea su z_index, que solo ordena dentro de cada capa. Sirve para que lo que va encima (ventanas, lupas, etiquetas, un cursor) no dependa de un z_index mayor que cualquiera del resto del diagrama. Una escena nombra sus capas una vez; repetir las mismas no hace nada. Sin nombres, con un nombre vacío o repetido, con un default que no está entre ellos o con capas distintas a las ya nombradas lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
namesstrobligatorioNombres de las capas, de atrás hacia adelante.
defaultstr | NoneNoneCapa de los objetos sin z_layer; sin él, la primera.
Devuelve: None
Ejemplo
scene.z_layers("fondo", "contenido", "overlay", default="contenido")
rejilla = scene.geometry.rect(14, 8).fill(GRAY).z_layer("fondo")
cursor = scene.geometry.dot(0.1).fill(GOLD).z_layer("overlay")
MÉTODOScene.persistpersist(object: Drawable, *others: Drawable) -> None
Mantiene objetos visibles y animables en los segmentos siguientes, desde el cursor actual. Las transiciones automáticas (cross_fade, slide…) no los afectan. Con el mismo z_index, un objeto persistente se dibuja encima del contenido de los segmentos, durante las transiciones y fuera de ellas; con un z_index menor queda debajo (una rejilla persistente con z_index(-1)), salvo en una transición por shader, que lo compone encima de la mezcla. Un objeto invisible sigue invisible hasta que una animación de entrada cambia su opacidad. Un objeto que no estaba en pantalla antes del segmento actual y se persiste al empezarlo entra con ese segmento: su transición de entrada lo revela como al resto del segmento, y pasa a ser global cuando termina. Para dejarlo fuera de esa transición, créalo y persístelo antes de scene.segment(...).
Parámetros
NombreTipoPredeterminadoDescripción
objectDrawableobligatorioPrimer objeto.
othersDrawable()Más objetos de la misma escena.
Devuelve: None
MÉTODOScene.releaserelease(object: Drawable, *others: Drawable) -> None
Termina la persistencia y devuelve los objetos al segmento activo. Al inicio de un segmento, el objeto queda quieto durante la transición de entrada y después pasa a ser local. Nunca oculta ni elimina el objeto: la siguiente transición lo trata como contenido saliente normal.
Parámetros
NombreTipoPredeterminadoDescripción
objectDrawableobligatorioPrimer objeto.
othersDrawable()Más objetos de la misma escena.
Devuelve: None
Ejemplo
from gaanim import BLUE, GOLD, Scene, Transition

scene = Scene(frame=(16, 9), background="#0f172a")
title = scene.text("Contexto compartido", role="title").fill(GOLD).move_to(0, 0.875)
scene.play([title.animate.write().duration(0.5)])

scene.segment("contenido", Transition.cross_fade(0.35))
scene.reuse(title)
dot = scene.geometry.dot(0.225).fill(BLUE).move_to(0, -0.375)
scene.play([dot.animate.grow_from_center().duration(0.4)])
scene.persist(title)

scene.segment("cierre", Transition.slide(0.35, "left"))
scene.release(title)
scene.wait(0.5)
scene.render()
animation preview

La identidad de una presentación (logo, pie, número de diapositiva) se configura una vez con scene.slides.brand(...); consulta Presentaciones.

Secciones

Section agrupa pasos de contenido que se construyen con funciones de Python. Cada paso abre su propio segmento; la función recibe la escena y crea contenido y pausas sin volver a llamar a segment. Estas clases se importan desde gaanim.

CLASESectionSection(key: str, steps: Sequence[SectionStep], *, title: str | None = None)
Una clave, un título o una lista de pasos vacíos lanzan ValueError; un paso de otro tipo, TypeError. section.build(scene, *, on_enter=None) abre cada segmento, llama a on_enter(scene, progress) y ejecuta la función del paso; devuelve los Segment en orden y no inserta pausas. Reutiliza la misma instancia para repetir la sección: el progreso vuelve a empezar y los nombres de segmento incluyen clave, visita, número y nombre del paso. Las excepciones se propagan sin deshacer lo ya creado.
Parámetros
NombreTipoPredeterminadoDescripción
keystrobligatorioIdentidad estable de la sección; única entre las secciones de una escena.
stepsSequence[SectionStep]obligatorioPasos en orden; se copian al crear la sección.
titlestr | NoneNoneNombre que muestran la agenda y la barra de progreso; sin él se usa key.
Devuelve: Section — Tiene key, title y steps.
Ejemplo
from gaanim import Scene, Section, SectionStep

scene = Scene(frame=(16, 9), theme="technical")

def contexto(scene):
    title = scene.text("Contexto", size=0.5)
    scene.play(title.animate.write())
    scene.stop()

def propuesta(scene):
    title = scene.text("Propuesta", size=0.5)
    scene.play(title.animate.write())
    scene.stop()

section = Section("introduccion", [
    SectionStep(name="Contexto", build=contexto, notes="Presenta el problema."),
    SectionStep(name="Propuesta", build=propuesta),
])

def al_entrar(scene, progress):
    print(progress.key, progress.index, progress.total, progress.fraction)

segments = section.build(scene, on_enter=al_entrar)
scene.render()
introduccion 1 2 0.5
introduccion 2 2 1.0
CLASESectionStepSectionStep(*, name: str, build: Callable[[Scene], None], transition: Transition | None = None, notes: str | None = None, template: Callable[..., Layout] | None = None, background: BackgroundLike | None = None)
Un nombre vacío lanza ValueError; una función o plantilla que no se puede llamar, TypeError.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre del paso.
buildCallable[[Scene], None]obligatorioFunción que crea el contenido del paso.
transition, notes, template, backgroundNoneIgual que en Scene.segment.
Devuelve: SectionStep — Valor inmutable.
CLASESectionProgressSectionProgress(key, step, index, total, visit, segment)
key y step identifican la sección y el paso; index (desde 1) y total cuentan los pasos; visit (desde 1) cuenta las repeticiones de la sección; segment es el Segment recién abierto, útil para bind. fraction vale index / total. Las pausas dentro de un paso no avanzan el progreso.
Devuelve: SectionProgress — Contexto que recibe on_enter.
PROPIEDADScene.sectionssections: SceneSections
Construyen las dos piezas de navegación de una charla sobre una base neutra de colores del tema. Cada parte es un drawable normal que puedes reestilizar, animar u ocultar. Las secciones pueden ser instancias de Section, claves o pares (clave, título).
Devuelve: SceneSections — Fábricas agenda(...) y progress_rail(...).
Ejemplo
agenda = scene.sections.agenda(sections, pitch=0.66)
agenda.root.move_to(-2, 1.4, Anchor.TOP_LEFT)
rail = scene.sections.progress_rail(sections, segmented=True, captions=True)
rail.root.move_to(0, -4.3).hud()
scene.persist(rail.root)

for section in sections:
    scene.play([agenda.animate.focus(section), rail.animate.enter(section)])
    section.build(scene, on_enter=lambda scene, p: scene.play(rail.animate.to(p)))

Agenda. scene.sections.agenda(sections, current=None, *, direction="column",
pitch=0.5, styles=None, item=None, marker=None, marker_gap=0.3)
devuelve un Agenda. Cada entrada está en uno de tres estados, done, current o upcoming, y se coloca por su punto izquierdo-central a pitch unidades de la anterior, en columna o en fila. Cada entrada guarda un drawable por estado y solo muestra el suyo, así que un estado puede cambiar peso, color o contenido: focus hace un fundido cruzado entre ellos. Por defecto done usa foreground, current usa accent con peso 700 y upcoming usa muted; styles={"current": TextStyle(...)} sustituye cualquiera de ellos e item=lambda scene, entry, state: ... sustituye el texto por cualquier drawable. marker=lambda scene: ... añade un indicador a marker_gap de la entrada actual que se desliza con focus. Para anotar esas funciones, NavigationEntry y NavigationState se importan desde gaanim.

  • agenda.item(key) es el grupo de una entrada, agenda.items("done") las entradas en un estado y agenda.variant(key, "current") el drawable de ese estado.
  • agenda.focus(key) y agenda.advance(steps) cambian el estado de inmediato; agenda.animate.focus(key) y agenda.animate.advance() devuelven composiciones para scene.play, con easing= opcional. Los destinos aceptan una clave, un índice desde 0, un Section o un SectionProgress.

Barra de progreso. scene.sections.progress_rail(sections=None, *,
length=12.0, thickness=0.08, orientation="horizontal", segmented=False, ...)
devuelve un ProgressRail. Una barra continua es una sola pista con una marca en cada límite de sección; segmented=True da a cada sección su propia pista, separadas por gap. rail.animate.to(progress) llena las secciones anteriores a un SectionProgress y su parte de pasos; un número en [0, 1] fija la fracción de toda la barra. rail.animate.enter(section) marca una sección como actual sin llenar nada, como en una diapositiva divisoria. captions=True nombra cada sección sobre su tramo y la colorea por estado. Las partes son track, fills, marks, captions y label, y los argumentos track=, mark= y caption= sustituyen sus formas. Las barras se llenan de izquierda a derecha, o hacia arriba con orientation="vertical".

Salida

MÉTODOScene.renderrender() -> None
Llámalo una vez, al final del script. Qué se hace con la escena (previsualizarla, validarla, exportarla o capturarla) lo decide el comando que ejecuta el script. Fuera de la aplicación de Gaanim, por ejemplo con python main.py, lanza RuntimeError.
Devuelve: None — Entrega la línea de tiempo al ejecutable de Gaanim.
Ejemplo
from gaanim import Scene

scene = Scene(frame=(16, 9))
scene.wait(0.5)
scene.render()
MÉTODOScene.snapshotssnapshots(directory: str, times: Sequence[float]) -> int
Pide al ejecutable que capture seeks exactos para la comparación visual. Sin gaanim --diff, o con otra ruta, lanza RuntimeError. Consulta Capturas y comparación visual.
Parámetros
NombreTipoPredeterminadoDescripción
directorystrobligatorioLa ruta que gaanim --diff pasa en GAANIM_SNAPSHOTS.
timesSequence[float]obligatorioInstantes absolutos que se capturan.
Devuelve: int — Número de fotogramas capturados.
Ejemplo
import os

if snapshots := os.environ.get("GAANIM_SNAPSHOTS"):
    scene.snapshots(snapshots, [0.0, 1.0])
MÉTODOScene.thumbnailthumbnail(time: Optional[float] = None) -> None
Elige el fotograma que un archivo .gaanim guarda como portada, la imagen que el explorador de archivos muestra tras gaanim register, y la imagen del proyecto en el Inicio. Sin llamarlo, la portada es el fotograma de la primera pausa o, sin pausas, el del primer segmento que más muestra. Un time negativo o no finito lanza ValueError. Consulta Compartir sin Python.
Parámetros
NombreTipoPredeterminadoDescripción
timefloat | NoneNoneInstante de la línea de tiempo, en segundos; None toma el cursor.
Devuelve: None — Solo marca el instante.
Ejemplo
titulo = scene.text("Resultados").scale_by(2)
scene.play(titulo.animate.write())
scene.thumbnail()

Variación reproducible

MÉTODOScene.randomrandom(seed: int = 0) -> Random
La misma semilla da los mismos valores en cualquier plataforma, así que la escena sale igual en la previsualización y en la exportación.
Parámetros
NombreTipoPredeterminadoDescripción
seedint0Semilla del generador.
Devuelve: Random — Un generador con semilla.
Ejemplo
rng = scene.random(seed=42)
stars = [scene.geometry.dot(0.04).move_to(rng.uniform(-7, 7), rng.uniform(-4, 4)) for _ in range(60)]
MÉTODORandom.uniformuniform(low: float = 0.0, high: float = 1.0) -> float
high < low lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
lowfloat0.0Límite inferior, incluido.
highfloat1.0Límite superior, excluido.
Devuelve: float — Un número en [low, high).
MÉTODORandom.gaussgauss(mean: float = 0.0, std: float = 1.0) -> float
Una desviación negativa lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
meanfloat0.0Media.
stdfloat1.0Desviación típica.
Devuelve: float — Un valor de una distribución normal.
MÉTODORandom.integerinteger(low: int, high: int) -> int
Un rango vacío lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
lowintobligatorioLímite inferior, incluido.
highintobligatorioLímite superior, excluido.
Devuelve: int — Un entero en [low, high).
MÉTODORandom.choicechoice(items: Sequence[Any]) -> Any
Una secuencia vacía lanza IndexError.
Parámetros
NombreTipoPredeterminadoDescripción
itemsSequence[Any]obligatorioElementos entre los que elegir.
Devuelve: Any — Uno de los elementos.
MÉTODORandom.shuffleshuffle(items: list[Any]) -> None
Parámetros
NombreTipoPredeterminadoDescripción
itemslist[Any]obligatorioLista que se baraja.
Devuelve: None — Modifica la lista.
Ejemplo
rng = scene.random(seed=7)
colors = [BLUE, GOLD, CYAN]
rng.shuffle(colors)
size = rng.gauss(1.0, 0.1)
count = rng.integer(3, 8)
accent = rng.choice(colors)
PROPIEDADRandom.seedseed: int
Devuelve: int — La semilla con la que se creó el generador.
MÉTODOScene.noisenoise(*, frequency: float = 1.0, amplitude: float = 1.0, octaves: int = 1, seed: int = 0, center: float = 0.0) -> Computed
Ruido simplex fractal con semilla, evaluado de forma nativa en cada fotograma sin llamar a Python; la reproducción, los seeks y la exportación coinciden. Valores inválidos lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
frequencyfloat1.0Rapidez con que cambia el valor.
amplitudefloat1.0El valor queda en [-amplitude, amplitude] alrededor de center.
octavesint1Capas de detalle fino, de 1 a 8.
seedint0Semilla del ruido.
centerfloat0.0Valor central.
Devuelve: Computed — Un escalar reactivo que depende del tiempo de la línea de tiempo.
Ejemplo
drift = scene.noise(frequency=0.6, amplitude=0.3, octaves=3, seed=5)
title.rotate_to(computed(lambda v: 0.1 * v, inputs=[drift]))
scene.wait(2)

Cámara

scene.camera controla qué parte del mundo se ve. Sus métodos directos aplican un corte en el cursor, sin avanzar el tiempo, y devuelven la cámara para encadenar. Los mismos métodos bajo scene.camera.animate devuelven un Anim que se programa con scene.play junto a cualquier otra animación y admite .duration(), .delay() y .easing().

from gaanim import BLUE, GOLD, Scene

scene = Scene(frame=(16, 9), background="#0f172a")
circle = scene.geometry.circle(0.8).fill(BLUE).move_to(-4, 1)
square = scene.geometry.square(1.2).fill(GOLD).move_to(4, -1)
scene.play([scene.camera.animate.frame_to(circle, margin=0.6).duration(0.8)])
scene.play([scene.camera.animate.frame_to(square, margin=0.6).duration(0.8)])
scene.play([scene.camera.animate.reset().duration(0.6)])
scene.render()
animation preview

Los destinos de posición (pan_to, follow, look_at, bind_*) aceptan un Drawable, un AnchorPoint, un PointRef o una tupla 2D o 3D. El zoom y la rotación aceptan también Parameter, Variable y Computed. Las APIs de cámara rechazan valores no finitos, zooms, campos de visión y planos de recorte inválidos, poses de look_at degeneradas e influencias fuera de [0, 1] con ValueError, sin recortar valores en silencio.

Poses guardadas

MÉTODOCamera.state_2dstate_2d(center: tuple[float, float] = (0.0, 0.0), zoom: float = 1.0, rotation: float = 0.0) -> CameraState
Valida la pose sin avanzar la línea de tiempo. Un CameraState pertenece a la escena que lo creó y no depende del tamaño del lienzo ni de la ventana.
Parámetros
NombreTipoPredeterminadoDescripción
centertuple[float, float](0.0, 0.0)Punto que queda en el centro de la vista.
zoomfloat1.0Zoom ortográfico; más de 1 acerca.
rotationfloat0.0Giro de la vista en radianes.
Devuelve: CameraState — Una pose ortográfica reutilizable.
Ejemplo
detail = scene.camera.state_2d(center=(-1.6, 0.4), zoom=1.5)
scene.play([scene.camera.animate.to(detail).duration(0.8)])
MÉTODOCamera.capturecapture() -> CameraState
No tiene duración. Registra la pose escrita en el script antes de los bindings, los efectos temporales, el shake y la vista de inspección del editor, así que restaurarla da el mismo resultado al previsualizar, buscar y exportar.
Devuelve: CameraState — La pose que la cámara tiene en el cursor.
MÉTODOCamera.savesave(name: str) -> CameraState
Captura la pose actual y la guarda con un nombre para restore.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre no vacío; reemplaza una pose guardada con el mismo nombre.
Devuelve: CameraState — La pose capturada.
MÉTODOCamera.toto(state: CameraState) -> Camera
Salta a la pose en el cursor. Una pose de otra escena lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
stateCameraStateobligatorioPose de esta escena.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.restorerestore(name: str) -> Camera
Salta a una pose guardada. Un nombre desconocido lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre usado en save.
Devuelve: Camera — La misma cámara.
Ejemplo
scene.camera.save("general")
scene.camera.zoom_to(2.0)
scene.wait(0.5)
scene.camera.restore("general")

Encuadre

MÉTODOCamera.pan_topan_to(x: float, y: float) -> Camera | pan_to(target: Endpoint) -> Camera
Parámetros
NombreTipoPredeterminadoDescripción
x, yfloatobligatorioPunto que queda en el centro de la vista.
targetEndpointobligatorioObjeto, punto de anclaje o tupla que se centra.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.zoom_tozoom_to(zoom: ScalarSource) -> Camera
Parámetros
NombreTipoPredeterminadoDescripción
zoomScalarSourceobligatorioZoom ortográfico; más de 1 acerca y menos de 1 aleja. Debe ser positivo.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.frame_toframe_to(targets: Drawable | Sequence[Drawable], margin: float | tuple[float, float] | tuple[float, float, float, float] | None = None, *, dynamic: bool = False) -> Camera
Centra y ajusta el zoom para que los objetos quepan en la vista.
Parámetros
NombreTipoPredeterminadoDescripción
targetsDrawable | Sequence[Drawable]obligatorioObjetos que deben caber en la vista.
marginfloat | tupleNoneMargen alrededor en unidades de escena: un valor, (vertical, horizontal) o cuatro lados en el orden de CSS. Sin él, media unidad por lado.
dynamicboolFalseRecalcula la unión en cada fotograma, después de los updaters y del layout.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.rotate_torotate_to(angle: ScalarSource) -> Camera
Parámetros
NombreTipoPredeterminadoDescripción
angleScalarSourceobligatorioGiro de la vista en radianes.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.orthographicorthographic(zoom: float = 1.0) -> Camera
Selecciona la proyección ortográfica, la de una escena 2D.
Parámetros
NombreTipoPredeterminadoDescripción
zoomfloat1.0Zoom positivo.
Devuelve: Camera — La misma cámara.
MÉTODOCamera.resetreset() -> Camera
Restaura la pose 2D predeterminada, el vector vertical, el objetivo y la proyección ortográfica.
Devuelve: Camera — La misma cámara.
Ejemplo
scene.camera.pan_to(circle).zoom_to(1.5).rotate_to(0.1)
scene.wait(0.5)
scene.camera.frame_to(circle, margin=0.4)
scene.wait(0.5)
scene.camera.reset()
PROPIEDADCamera.animateanimate: CameraAnimation
Cada método devuelve un Anim nuevo; construirlo no cambia nada hasta pasarlo a scene.play. Un movimiento dura un segundo salvo que .duration() fije otra; la duration de scene.play sustituye ese valor por defecto, como en las demás animaciones. shake (medio segundo la sacudida senoidal) y whip_pan (0,4 s) tienen sus propios valores por defecto, que scene.play también sustituye; una sacudida con trauma dura hasta apagarse.
Devuelve: CameraAnimation — El vocabulario animado de la cámara.

Movimientos animados

MÉTODOCameraAnimation.toto(state: CameraState) -> Anim
Entre una pose ortográfica y una en perspectiva, el movimiento va en perspectiva desde o hacia la vista equivalente a la ortográfica, y solo es ortográfico en su extremo ortográfico: el contenido 3D queda siempre delante del ojo. Una pose de otra escena lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
stateCameraStateobligatorioPose de esta escena.
Devuelve: Anim — Movimiento hasta la pose.
MÉTODOCameraAnimation.restorerestore(name: str) -> Anim
Un nombre desconocido lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
namestrobligatorioNombre usado en Camera.save.
Devuelve: Anim — Movimiento hasta la pose guardada.
Ejemplo
scene.camera.save("general")
scene.play([scene.camera.animate.zoom_to(2.0).duration(0.6)])
scene.play([scene.camera.animate.restore("general").duration(0.6)])
MÉTODOCameraAnimation.pan_topan_to(x: float, y: float) -> Anim | pan_to(target: Endpoint) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
x, yfloatobligatorioPunto que termina en el centro de la vista.
targetEndpointobligatorioObjeto, punto de anclaje o tupla que se centra.
Devuelve: Anim — Desplazamiento de la vista.
Ejemplo
scene.play([scene.camera.animate.pan_to(-1.6, 0.4).duration(0.8)])
MÉTODOCameraAnimation.zoom_tozoom_to(zoom: ScalarSource, *, interpolation: Literal["exponential", "linear"] = "exponential") -> Anim
Con "exponential" el zoom sigue z0 * (z1 / z0) ** p, donde p es el progreso con easing: el área visible cambia en la misma proporción en cada fotograma y un zoom de 8× se lee a velocidad constante. "linear" usa z0 + (z1 - z0) * p. Un zoom constante no positivo o una interpolación desconocida lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
zoomScalarSourceobligatorioZoom final; más de 1 acerca.
interpolation"exponential" | "linear""exponential"Cómo se interpola el zoom.
Devuelve: Anim — Zoom animado.
Ejemplo
scene.play([scene.camera.animate.zoom_to(8.0).duration(1.5)])
scene.play([scene.camera.animate.zoom_to(1.0, interpolation="linear").duration(0.8)])
MÉTODOCameraAnimation.frame_toframe_to(targets: Drawable | Sequence[Drawable], margin: float | tuple[float, float] | tuple[float, float, float, float] | None = None, *, dynamic: bool = False, interpolation: Literal["exponential", "linear"] = "exponential") -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
targetsDrawable | Sequence[Drawable]obligatorioObjetos que deben caber en la vista.
marginfloat | tupleNoneIgual que en Camera.frame_to.
dynamicboolFalseSigue a los objetos si se mueven durante el movimiento, y al terminar la cámara conserva el último encuadre.
interpolation"exponential" | "linear""exponential"Con "exponential" el zoom es exponencial y el desplazamiento acompaña al cambio de ancho visible, así que la vista escala alrededor de un punto fijo y el contenido viaja en línea recta. "linear" interpola posición y zoom por separado.
Devuelve: Anim — Encuadre animado.
Ejemplo
scene.play([scene.camera.animate.frame_to([circle, label], margin=(0.32, 0.48), dynamic=True).duration(0.9)])
MÉTODOCameraAnimation.rotate_torotate_to(angle: ScalarSource) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
angleScalarSourceobligatorioGiro final de la vista en radianes.
Devuelve: Anim — Giro animado.
Ejemplo
scene.play([scene.camera.animate.rotate_to(0.15).duration(0.5)])
MÉTODOCameraAnimation.followfollow(target: Endpoint, *, offset: tuple[float, float] = (0.0, 0.0), offset_space: Literal["world", "local"] = "world", lag: float = 0.0) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
targetEndpointobligatorioObjeto o punto que se sigue.
offsettuple[float, float](0.0, 0.0)Desplazamiento de la vista respecto del objetivo.
offset_space"world" | "local""world""local" gira el desplazamiento con el objetivo.
lagfloat0.0Retraso de seguimiento en segundos, determinista.
Devuelve: Anim — Seguimiento durante la duración del Anim. Al terminar, la cámara se queda en la pose que alcanzó, calculada con la posición del objetivo al final del play, y el siguiente movimiento parte de ahí.
Ejemplo
scene.play([
    circle.animate.move_to(4, 0).duration(2.0),
    scene.camera.animate.follow(circle.anchor_point(Anchor.TOP), offset=(0, 0.24), lag=0.2).duration(2.0),
])
MÉTODOCameraAnimation.shakeshake(amplitude: Optional[float] = None, frequency: Optional[float] = None, *, trauma: Optional[float] = None, decay: Optional[float] = None, rotation: Optional[float] = None, seed: Optional[int] = None) -> Anim
Sigue el modelo de trauma: el desplazamiento es proporcional a trauma ** 2, así que un golpe leve apenas se nota y uno fuerte sacude mucho. Traslación y giro salen de ruido coherente con semilla. Dura trauma / decay segundos (un segundo con decay=0); un .duration() más corto también vuelve suavemente al reposo, y el easing no cambia el decaimiento. Se suma después de follow, encuadre y bindings, y es función pura del tiempo. Pasar solo amplitude (sin trauma, decay, rotation ni seed) mantiene la sacudida sinusoidal anterior: amplitude es el pico, frequency cuenta oscilaciones por clip (8 por defecto) y dura 0.5 s. Valores negativos o trauma mayor que 1 lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
amplitudefloat | None0.4Desplazamiento máximo en unidades de escena con trauma 1.
frequencyfloat | None12Frecuencia del ruido en Hz.
traumafloat | None0.8Intensidad inicial, en [0, 1].
decayfloat | None1.5Trauma que se pierde por segundo.
rotationfloat | None0.02Giro máximo en radianes con trauma 1.
seedint | None0Semilla del ruido.
Devuelve: Anim — Sacudida que termina en reposo.
Ejemplo
scene.play([scene.camera.animate.shake(trauma=0.8, decay=1.5, frequency=12, rotation=0.02, seed=0)])
scene.play([scene.camera.animate.shake(trauma=0.4, seed=3)])  # un golpe más leve
MÉTODOCameraAnimation.follow_pathfollow_path(route: Drawable, *, orient: bool = False, rotate_offset: float = 0.0, start: float = 0.0, end: float = 1.0) -> Anim
Lee la trayectoria en coordenadas de escena cuando se reproduce la animación, como Anim.move_along. El giro de orient se calcula en cada fotograma, así que nunca salta. Easing predeterminado: suave. Una ruta de otra escena, start o end fuera de [0, 1] o iguales, o un rotate_offset no finito lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
routeDrawableobligatorioDrawable cuya trayectoria recorre la cámara.
orientboolFalseGira la vista para que el sentido de avance apunte a la derecha de la pantalla.
rotate_offsetfloat0.0Giro extra en radianes; -math.pi / 2 apunta el avance hacia arriba.
start / endfloat0.0 / 1.0Tramo recorrido, como fracciones de longitud de arco; start > end lo recorre al revés.
Devuelve: Anim — Recorrido de la cámara.
Ejemplo
route = scene.geometry.path([(-6, -2), (-2, 2), (2, -2), (6, 2)])
scene.play([scene.camera.animate.follow_path(route, orient=True).duration(4)])
MÉTODOCameraAnimation.whip_panwhip_pan(to: Endpoint | float, y: Optional[float] = None, *, blur: bool = True) -> Anim
Paneo rápido de 0.4 s salvo .duration, con easing exponencial de entrada y salida: casi todo el recorrido ocurre en un instante a mitad de camino. Con blur, los fotogramas exportados y los snapshots del barrido reciben un desenfoque de movimiento de 360° y 24 subfotogramas aunque la escena no tenga uno propio; la vista previa sigue nítida, como con canvas.motion_blur.
Parámetros
NombreTipoPredeterminadoDescripción
to / yEndpoint | floatobligatorioDestino, como en pan_to.
blurboolTrueDesenfoque de movimiento en los fotogramas del barrido.
Devuelve: Anim — Barrido rápido.
Ejemplo
section_b = scene.text("B", role="title").move_to(16, 0)
scene.play([scene.camera.animate.whip_pan(section_b)])
scene.play([scene.camera.animate.whip_pan(0, 0, blur=False).duration(0.3)])
MÉTODOCameraAnimation.orthographicorthographic(zoom: float = 1.0) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
zoomfloat1.0Zoom positivo.
Devuelve: Anim — Paso animado a la proyección ortográfica. Desde una cámara en perspectiva, el ojo se aleja por su eje de visión mientras el campo de visión se cierra: el objetivo sigue centrado, la profundidad se aplana y la proyección pasa a ortográfica al terminar, con la misma pose. Desde una cámara ortográfica, anima el zoom.
Ejemplo
cube = scene.geometry.cube(2.0)
scene.camera.look_at(eye=(7, 5, 10), target=(0, 0, 0)).perspective(0.7)
scene.play([scene.camera.animate.orthographic(1.5)], duration=1.0)
MÉTODOCameraAnimation.resetreset() -> Anim
Devuelve: Anim — Vuelta animada a la pose y proyección predeterminadas. Desde una cámara en perspectiva, el movimiento sigue en perspectiva hacia la vista equivalente a la 2D y pasa a ortográfica al terminar.
Ejemplo
scene.play([scene.camera.animate.zoom_to(2.0).duration(0.5)])
scene.play([scene.camera.animate.reset().duration(0.5)])

Capas de parallax

Una capa de parallax agrupa drawables que están más lejos o más cerca de la cámara que el plano de la escena. Con la cámara ortográfica, sus miembros se desplazan 1 / depth del paneo de cámara: el fondo lejano se mueve despacio y el primer plano pasa rápido, sin 3D real. No confundir con Scene.z_layers (orden de dibujo con nombre), Drawable.view_layer (filtro de vistas de cámara) ni con .layer(nombre) de gráficos y ejes (una parte del objeto).

MÉTODOScene.layerlayer(depth: float) -> ParallaxLayer
La capa ve una cámara centrada en posición / depth y con zoom zoom ** (1 / depth); la rotación afecta a todas por igual. depth=1 se comporta como el resto de la escena, depth=3 se mueve a un tercio de la velocidad de la cámara, depth=0.6 pasa más rápido y math.inf no se desplaza ni hace zoom, como Drawable.hud salvo por la rotación. Sacudida, seguimiento y bindings también mueven las capas; un miembro convertido en HUD con Drawable.hud sigue fijo en pantalla. Las capas lejanas se dibujan detrás de las cercanas: su z_index por defecto es round(-100 * ln(depth)), limitado a ±900, y cada miembro suma el suyo. Con cámara en perspectiva las capas no se mueven, porque la profundidad real ya produce parallax. Una profundidad no positiva lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
depthfloatobligatorioDistancia a la cámara en unidades de la distancia del plano de la escena; positiva, math.inf permitido.
Devuelve: ParallaxLayer — Capa vacía; es un Drawable cuyos miembros se añaden con add.
Ejemplo
import math
from gaanim import *

scene = Scene(frame=(16, 9), background="#1d2b53")
sky = scene.layer(depth=math.inf).add(scene.geometry.circle(0.9).fill(GOLD).move_to(4.5, 2.6))
far = scene.layer(depth=3.0)
for i in range(6):
    far.add(scene.geometry.polygon([(-2.4, 0), (0, 2.6), (2.4, 0)]).fill("#3b4a7a").move_to(-8 + i * 4.2, -1.4))
for i in range(8):
    scene.geometry.rect(0.5, 1.2).fill("#8a5a3b").move_to(-6 + i * 3, -2.0)
near = scene.layer(depth=0.6)
for i in range(5):
    near.add(scene.geometry.circle(0.9).fill("#1f4d33").move_to(-7 + i * 5, -3.3))
scene.play([scene.camera.animate.pan_to(6, 0).duration(3)])
scene.render()
animation preview
MÉTODOParallaxLayer.addadd(drawable: Drawable, *others: Drawable) -> Self
Mueve los drawables a la capa conservando su posición actual; añadirlo a otra capa lo mueve allí. Mover, desvanecer o animar la capa actúa sobre todos sus miembros. Un drawable de otra escena, la propia capa u otra capa lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
drawableDrawableobligatorioPrimer drawable que entra en la capa.
*othersDrawableobligatorioMás drawables.
Devuelve: Self — La misma capa, para encadenar.
Ejemplo
far = scene.layer(depth=3.0)
far.add(scene.geometry.circle(1.0).move_to(4, 2), scene.geometry.square(1.0).move_to(-4, 2))
print(far.depth)  # 3.0
3.0

Profundidad de campo

Las capas de parallax pueden desenfocarse según su distancia al foco, como con una lente real: cada capa se desenfoca aperture * |1 - focus / depth| unidades de escena, como máximo max_blur. Las capas a la profundidad focus quedan nítidas y el desenfoque crece al alejarse de ella hacia delante o hacia atrás; una capa en math.inf se desenfoca aperture. Los drawables fuera de capas, y los HUD dentro de ellas, nunca se desenfocan. Cada capa se desenfoca como una sola imagen, con un efecto de shader en su raíz que reemplaza cualquier shader_effect puesto en la propia capa; una capa desenfocada se dibuja fuera de pantalla y cuesta más.

MÉTODOCamera.depth_of_fielddepth_of_field(focus: Optional[float] = None, *, aperture: Optional[float] = None, max_blur: Optional[float] = None) -> Camera
Fija la profundidad de campo en el cursor. Los valores omitidos se conservan; al principio el foco es 1, la apertura 0 (sin desenfoque) y max_blur 0.5. Un foco no finito o no positivo, o una aperture o max_blur negativos o no finitos lanzan ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
focusfloat | NoneNoneProfundidad nítida, finita y positiva; 1 es el plano de la escena.
aperturefloat | NoneNoneEscala del desenfoque, en unidades de escena; 0 lo apaga.
max_blurfloat | NoneNoneDesenfoque máximo, en unidades de escena.
Devuelve: Camera — La cámara.
Ejemplo
import math
from gaanim import *

scene = Scene(frame=(16, 9), background="#1d2b53")
far = scene.layer(depth=3.0)
for i in range(5):
    far.add(scene.geometry.polygon([(-2.4, 0), (0, 2.6), (2.4, 0)]).fill("#3b4a7a").move_to(-8 + i * 4.2, -1.2))
for i in range(6):
    scene.geometry.rect(0.5, 1.2).fill("#8a5a3b").move_to(-6.5 + i * 2.6, -2.0)
near = scene.layer(depth=0.5)
for i in range(4):
    near.add(scene.geometry.circle(0.9).fill("#1f4d33").move_to(-6 + i * 4.4, -3.4))
scene.camera.depth_of_field(focus=1.0, aperture=0.12)
scene.wait(0.5)
scene.play([scene.camera.animate.focus_to(3.0).duration(1.5)])
scene.render()
animation preview
MÉTODOCameraAnimation.depth_of_field / focus_to
depth_of_field anima los mismos valores que Camera.depth_of_field; focus_to(focus) es el cambio de foco (rack focus). El foco se mueve en profundidad inversa, así que pasa de forma pareja por capas cercanas y lejanas; la apertura y max_blur cambian linealmente. Easing predeterminado: suave.
Parámetros
NombreTipoPredeterminadoDescripción
focusfloatobligatorioNueva profundidad nítida.
Devuelve: Anim — Cambio animado de la profundidad de campo.
Ejemplo
scene.camera.depth_of_field(focus=0.6, aperture=0.12)
scene.play([scene.camera.animate.focus_to(3.0).duration(1.5)])
scene.play([scene.camera.animate.depth_of_field(aperture=0.0).duration(0.5)])

Bindings persistentes

Un binding es una restricción de cámara que no se dibuja: desde que se crea (salvo con enabled=False) escribe los canales que declara en cada fotograma, también a través de segmentos. influence es un escalar en [0, 1] que mezcla el binding con la pose anterior, y los bindings posteriores se componen sobre los anteriores. follow y el encuadre dinámico actúan después, y el shake se suma al final.

MÉTODOCamera.bind_2dbind_2d(*, center: Optional[Endpoint] = None, zoom: Optional[ScalarSource] = None, rotation: Optional[ScalarSource] = None, influence: Optional[ScalarSource] = None, enabled: bool = True) -> CameraConstraint
Necesita center, zoom o rotation, y selecciona la proyección ortográfica.
Parámetros
NombreTipoPredeterminadoDescripción
centerEndpoint | NoneNonePunto que sigue el centro de la vista.
zoomScalarSource | NoneNoneZoom reactivo.
rotationScalarSource | NoneNoneGiro reactivo en radianes.
influenceScalarSource | NoneNonePeso en [0, 1]; sin él, 1.
enabledboolTrueActivo desde el cursor actual.
Devuelve: CameraConstraint — El binding, con enable() y disable().
Ejemplo
import math

theta = scene.viz.parameter(0.0)
focus = scene.geometry.point_ref(
    computed(lambda t: t * 2.25, inputs=[theta]),
    computed(lambda t: math.sin(t * 2), inputs=[theta]),
)
rig = scene.camera.bind_2d(center=focus, zoom=computed(lambda t: 1 + t * 0.3, inputs=[theta]))
scene.play([theta.animate.set(1.0).duration(2.0)])
rig.disable()
MÉTODOCameraConstraint.enableenable() -> None
Devuelve: None — Activa el binding en el cursor actual.
MÉTODOCameraConstraint.disabledisable() -> None
Los cambios quedan registrados en la línea de tiempo: un seek anterior vuelve a verlo activo.
Devuelve: None — Desactiva el binding en el cursor actual.

Vistas de cámara

Una vista de cámara muestra dentro de una figura lo que ve una segunda cámara. Drawable.camera_view (en Drawable) convierte cualquier figura cerrada en pantalla; scene.camera.inset monta de una vez la pantalla, el marco y los conectores. Ambos devuelven un CameraView.

MÉTODOCamera.insetinset(target: Endpoint, *, zoom: float | Parameter = 2.0, at: Anchor | tuple[float, float] = Anchor.TOP_RIGHT, size: float | tuple[float, float] | None = None, aspect: Optional[float] = None, shape: Literal["rect", "rounded", "circle"] = "rounded", follow: bool = False, connectors: bool = True, color: Optional[ColorLike] = None, fixed: bool = False, background: Paint | Literal["canvas"] | None = "canvas", exclude: Sequence[Drawable] = (), layers: Sequence[str] = ()) -> CameraView
Muestra un detalle ampliado en una pantalla aparte. El marco sigue el zoom, así que siempre recuadra lo que la pantalla muestra en reposo. Lanza ValueError con un zoom o tamaño no positivo, un shape o at desconocido, o un target reactivo sin follow.
Parámetros
NombreTipoPredeterminadoDescripción
targetEndpointobligatorioQué ampliar: un punto, un objeto o un punto de anclaje. Con follow=True, cualquier extremo reactivo.
zoomfloat | Parameter2.0Aumento de la pantalla en reposo.
atAnchor | tuple[float, float]Anchor.TOP_RIGHTEsquina o borde del fotograma donde va la pantalla (Anchor.CENTER la centra), o punto donde se centra.
sizefloat | tuple[float, float] | NoneNoneAncho de la pantalla, o diámetro si es un círculo; por defecto, el 30 % del ancho de la escena (20 % para un círculo). size=(ancho, alto) fija ambos lados de un rectángulo.
aspectfloat | NoneNoneProporción ancho / alto de un rectángulo y su marco; por defecto, la del fotograma. Úsala para encuadrar entero un panel alto o una barra de herramientas. Un círculo no admite aspect.
shapestr"rounded""rect", "rounded" o "circle".
followboolFalseEl marco sigue a target mientras se mueve.
connectorsboolTrueDos líneas que unen las esquinas enfrentadas del marco y la pantalla.
colorColor | NoneNoneTrazo de la pantalla, el marco y los conectores; por defecto, el acento del tema.
fixedboolFalseFija la pantalla a la imagen como un HUD, para que no se mueva con scene.camera.
background / exclude / layersobligatorioComo en Drawable.camera_view. Los conectores nunca aparecen en la vista.
Devuelve: CameraView — La vista, con sus connectors.
Ejemplo
from gaanim import Anchor, CYAN, GOLD, Scene
scene = Scene(frame=(16, 9), background="#0f172a")
for i in range(24):
    scene.geometry.dot(0.05).fill(CYAN if i % 2 else GOLD).move_to(-6 + 0.25 * i, -1 + 0.15 * (i % 5))
view = scene.camera.inset((-4, -0.7), zoom=4, at=Anchor.TOP_RIGHT)
scene.play([view.animate.pop_out().duration(0.8)])
scene.play([view.animate.pan_to(-1.5, -0.7).duration(1.2), view.animate.zoom_to(8).duration(1.2)])
scene.render()
animation preview
PROPIEDADCameraView.screenscreen: Drawable
Devuelve: Drawable — La figura que muestra la vista.
PROPIEDADCameraView.frameframe: Drawable
Devuelve: Drawable — El objeto que hace de cámara; se anima como cualquier otro.
PROPIEDADCameraView.connectorsconnectors: list[Drawable]
Devuelve: list[Drawable] — Las líneas de un inset; vacía en otras vistas.
PROPIEDADCameraView.zoomzoom: Parameter | Computed
Una vista creada con un número devuelve un Computed; con un Parameter o Computed, lo devuelve tal cual. Si el tamaño del marco fija el zoom, lanza ValueError.
Devuelve: Parameter | Computed — El zoom como valor reactivo, para lecturas y vínculos.
Ejemplo
label = scene.viz.readout(view.zoom, format=".1f", prefix="x")
MÉTODOCameraView.pan_topan_to(x: float, y: float) -> CameraView
Apunta la cámara en el cursor actual: mueve el centro del marco.
Parámetros
NombreTipoPredeterminadoDescripción
x, y / targetfloat / EndpointobligatorioCoordenadas, un objeto o un punto de anclaje.
Devuelve: CameraView — La misma vista.
MÉTODOCameraView.zoom_tozoom_to(zoom: float) -> CameraView
Fija el zoom en el cursor actual. Si el tamaño del marco fija el zoom, escala el marco para alcanzarlo, medido con su fit sobre ambos tamaños en ese punto. Un zoom no positivo, o una vista que sigue un Computed, lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
zoomfloatobligatorioAumento, mayor que cero.
Devuelve: CameraView — La misma vista.
MÉTODOCameraView.rotate_torotate_to(radians: ScalarSource) -> CameraView
Parámetros
NombreTipoPredeterminadoDescripción
radiansfloatobligatorioGiro de la cámara; la vista gira en sentido contrario.
Devuelve: CameraView — La misma vista.
MÉTODOCameraView.followfollow(target: Endpoint, *, offset: tuple[float, float] = (0.0, 0.0)) -> CameraView
Mantiene la cámara sobre target desde el cursor; pan_to deja de tener efecto después, y los movimientos anteriores se siguen viendo.
Parámetros
NombreTipoPredeterminadoDescripción
targetEndpointobligatorioQué seguir.
offsettuple[float, float](0, 0)Desplazamiento en unidades de escena.
Devuelve: CameraView — La misma vista.
PROPIEDADCameraView.animateanimate: CameraViewAnimation
Devuelve: CameraViewAnimation — Proxy de animación; sus resultados se pasan a scene.play.
MÉTODOCameraViewAnimation.zoom_tozoom_to(zoom: float) -> Anim
El zoom propio de la vista cambia a velocidad percibida constante; un Parameter se anima de forma lineal y una vista con zoom de marco escala el marco.
Parámetros
NombreTipoPredeterminadoDescripción
zoomfloatobligatorioAumento final.
Devuelve: Anim — La animación.
MÉTODOCameraViewAnimation.pan_to / rotate_to
Versiones animadas de CameraView.pan_to y CameraView.rotate_to.
Devuelve: Anim — La animación.
MÉTODOCameraViewAnimation.pop_outpop_out() -> Anim
La pantalla sale de la región que ve su cámara y crece hasta su sitio. Encogida sobre esa región muestra la escena a tamaño real, así que la vista brota de la escena sin saltos. Si es la primera animación de la pantalla, hace de entrada: hasta que empieza, la pantalla (y en un inset, el marco y los conectores) está oculta. Tras pop_in, la devuelve a donde estaba y los vuelve a mostrar.
Devuelve: Anim — La animación.
MÉTODOCameraViewAnimation.pop_inpop_in() -> Anim
Encoge la pantalla de vuelta a la región que ve su cámara y, al llegar, la oculta junto con el marco y los conectores de un inset, así que una capa de vista deja de verse sobre la escena. Es la salida simétrica de pop_out; el siguiente pop_out los vuelve a mostrar. En una vista de camera_view, el marco es tuyo y conserva su visibilidad.
Devuelve: Anim — La animación.

Cámara 3D

La cámara 3D usa coordenadas de mundo (x, y, z) y ángulos en radianes. Las primitivas 3D están en Geometría. Para una introducción, consulta la guía Cámara y 3D.

MÉTODOCamera.state_3dstate_3d(eye: tuple[float, float, float], target: tuple[float, float, float], up: tuple[float, float, float] = (0.0, 1.0, 0.0), fov_y: float = 0.7853981633974483, near: float = 0.1, far: float = 1000.0) -> CameraState
Parámetros
NombreTipoPredeterminadoDescripción
eyetuple[float, float, float]obligatorioPosición de la cámara.
targettuple[float, float, float]obligatorioPunto al que mira.
uptuple[float, float, float](0.0, 1.0, 0.0)Dirección vertical del mundo.
fov_yfloat0.785…Campo de visión vertical en radianes (π/4).
near, farfloat0.1, 1000.0Planos de recorte, con 0 < near < far.
Devuelve: CameraState — Una pose en perspectiva reutilizable.
MÉTODOCamera.perspectiveperspective(fov_y: float, near: float = 0.1, far: float = 1000.0) -> Camera
Cambia a proyección en perspectiva en el cursor.
Parámetros
NombreTipoPredeterminadoDescripción
fov_yfloatobligatorioCampo de visión vertical en radianes, en (0, pi).
nearfloat0.1Plano de recorte cercano, positivo.
farfloat1000.0Plano de recorte lejano, mayor que near.
Devuelve: Camera — La misma cámara.
Ejemplo
scene.camera.perspective(fov_y=0.785, near=0.1, far=1000.0)
MÉTODOCamera.look_atlook_at(eye: Endpoint, target: Endpoint, up: Optional[tuple[float, float, float]] = None) -> Camera
Coloca la cámara en eye mirando a target. Los extremos se resuelven después del layout reactivo; eye y target deben ser distintos y up no puede ser nulo ni paralelo a la dirección de la mirada.
Parámetros
NombreTipoPredeterminadoDescripción
eyeEndpointobligatorioPosición de la cámara en el mundo.
targetEndpointobligatorioPunto al que mira.
uptuple[float, float, float] | NoneNoneDirección vertical; (0, 1, 0) si se omite.
Devuelve: Camera — La misma cámara.
Ejemplo
scene.camera.look_at(eye=(7, 5, 6), target=(0, 0, 0))
MÉTODOCamera.bind_3dbind_3d(*, eye: Optional[Endpoint] = None, target: Optional[Endpoint] = None, fov_y: Optional[ScalarSource] = None, up: tuple[float, float, float] = (0.0, 1.0, 0.0), influence: Optional[ScalarSource] = None, enabled: bool = True) -> CameraConstraint
Necesita eye, target o fov_y, y selecciona la proyección en perspectiva.
Parámetros
NombreTipoPredeterminadoDescripción
eyeEndpoint | NoneNonePosición reactiva de la cámara.
targetEndpoint | NoneNonePunto reactivo al que mira.
fov_yScalarSource | NoneNoneCampo de visión reactivo en radianes.
uptuple[float, float, float](0.0, 1.0, 0.0)Dirección vertical.
influenceScalarSource | NoneNonePeso en [0, 1].
enabledboolTrueActivo desde el cursor actual.
Devuelve: CameraConstraint — El binding.
Ejemplo
rig = scene.camera.bind_3d(eye=(6, 4, 8), target=(0, 0, 0), fov_y=0.8, influence=0.75)
scene.wait(1)
rig.disable()
MÉTODOCameraAnimation.look_atlook_at(eye: Endpoint, target: Endpoint, up: Optional[tuple[float, float, float]] = None) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
eyeEndpointobligatorioPosición final de la cámara.
targetEndpointobligatorioPunto al que mira al final.
uptuple[float, float, float] | NoneNoneDirección vertical; (0, 1, 0) si se omite.
Devuelve: Anim — Movimiento animado de la cámara.
MÉTODOCameraAnimation.orbitorbit(delta_yaw: float, delta_pitch: float) -> Anim
Usa incrementos pequeños para un giro suave.
Parámetros
NombreTipoPredeterminadoDescripción
delta_yawfloatobligatorioGiro horizontal alrededor del objetivo, en radianes.
delta_pitchfloatobligatorioGiro vertical, en radianes.
Devuelve: Anim — Órbita alrededor del objetivo actual de look_at.
Ejemplo
scene.play([scene.camera.animate.orbit(delta_yaw=0.5, delta_pitch=0.1).duration(1.0)])
MÉTODOCameraAnimation.perspectiveperspective(fov_y: float, near: float = 0.1, far: float = 1000.0) -> Anim
Parámetros
NombreTipoPredeterminadoDescripción
fov_yfloatobligatorioCampo de visión vertical final, en radianes.
nearfloat0.1Plano cercano.
farfloat1000.0Plano lejano.
Devuelve: Anim — Paso animado a la perspectiva.
MÉTODOCameraAnimation.dollydolly(factor: float) -> Anim
Un factor menor que 1 acerca la cámara y uno mayor la aleja.
Parámetros
NombreTipoPredeterminadoDescripción
factorfloatobligatorioMultiplicador positivo de la distancia al objetivo.
Devuelve: Anim — Acercamiento o alejamiento.
Ejemplo
scene.play([scene.camera.animate.dolly(factor=0.85).duration(0.6)])
MÉTODOCameraAnimation.dolly_zoomdolly_zoom(factor: float) -> Anim
La cámara avanza o retrocede por su línea de visión hasta factor veces su distancia al objetivo de look_at mientras el campo de visión cambia para que el plano del objetivo conserve su tamaño en pantalla: el fondo se estira (factor mayor que 1) o se precipita. La distancia cambia exponencialmente. Solo el contenido 3D lo muestra, porque la capa 2D no se mueve con una cámara en perspectiva. Sin perspective o con la cámara sobre su objetivo avisa y no hace nada. Un factor no finito o no positivo lanza ValueError.
Parámetros
NombreTipoPredeterminadoDescripción
factorfloatobligatorioMultiplicador positivo de la distancia al objetivo.
Devuelve: Anim — Efecto vértigo.
Ejemplo
scene.camera.perspective(fov_y=0.6)
scene.camera.look_at(eye=(0, 1, 8), target=(0, 0, 0))
scene.play([scene.camera.animate.dolly_zoom(1.8).duration(2.0)])

Cámara de inspección del editor

El editor tiene una cámara de inspección separada de scene.camera, que nunca cambia las capturas, Presenter View ni la exportación. Empieza desactivada; cada vez que se activa con I (o con el botón Interactive de la barra de overlays, O) parte de una copia de la cámara de la escena en ese instante.

  • Num0: alterna entre Free 3D y Camera View (la cámara de la escena).
  • En 3D, arrastrar con el botón derecho: orbitar; con el central o Mayús + izquierdo: desplazar. En 2D, arrastrar con cualquier botón desplaza la vista y la escena sigue al cursor; un clic sin arrastrar selecciona el objeto.
  • Rueda: hacia arriba acerca y hacia abajo aleja. En 2D acerca hacia el punto bajo el cursor, que se queda quieto.
  • W, A, S, D: desplazan la cámara. Las flechas siguen navegando entre paradas y escenas.
  • Mientras la inspección está activa, un clic en la vista previa no avanza a la siguiente parada; Enter y las flechas sí. Arrastrar o usar la rueda sobre los paneles del editor no mueve la cámara.
  • F: encuadra la selección, o toda la escena si no hay nada seleccionado.
  • R: restablece y encuadra; I: activa o desactiva la inspección.

El marco de salida visible conserva el marco lógico y su proporción, y la selección con el ratón se limita a ese marco. Mientras la escena tiene contenido 3D, la barra de tiempo desactiva el ajuste magnético.

Overlays del editor

O muestra una barra con guías que se dibujan solo en el editor, nunca en las capturas ni en la exportación. O o Esc la ocultan.

  • Bounds (B): el marco de salida y su tamaño en unidades lógicas.
  • Coordinates (C): los ejes con sus marcas y el punto bajo el cursor en unidades de la escena. Mantén Mayús para ajustarlo a la grilla y pulsa Ctrl+C (Cmd+C en macOS) para copiarlo como tupla, lista para pegar en move_to((x, y)). La etiqueta se oculta sobre los paneles del editor.
  • Grid (G): líneas cada 1, 2 o 5 unidades (o sus potencias de diez), según el zoom. Durante la inspección cubren toda la vista, no solo el marco. En 3D es una grilla sobre el plano XZ alrededor del punto que mira la cámara, con el eje X en rojo y el Z en azul.
  • Guides (M): márgenes seguros de acción (90 %) y de títulos (80 %), y los tercios del marco.
  • Layout (K): inspecciona las cajas como las herramientas de desarrollo de un navegador. Cada caja lleva un contorno discontinuo y las zonas del segmento actual un contorno turquesa con su nombre. Al pasar el cursor por una caja se colorean su padding (verde), las celdas de sus hijos (azul), sus márgenes (naranja) y los gaps entre ellos (morado), con una etiqueta de su tipo, tamaño, padding y gap en px de diseño. Sigue la caja en cada momento de la línea de tiempo, también mientras se reorganiza.
  • El objeto seleccionado se enmarca con su centro y su tamaño en unidades, y el Inspector muestra sus propiedades (ver más abajo).
  • Con la inspección activa, la barra muestra el zoom de la vista (o Free 3D) en azul cuando ya no coincide con la cámara de la escena, y un botón para restablecerla (R). El botón de captura guarda el fotograma actual como PNG, sin overlays (Ctrl+Mayús+S). El icono de teclado lista todos los atajos.

El editor recuerda qué overlays dejaste activados entre sesiones; el modo en sí siempre empieza oculto.

Inspector, consola y animaciones

Con los overlays visibles, un clic en la vista previa selecciona un objeto y el Inspector (botón de la barra) muestra:

  • su tipo con el vocabulario del script (circle, rounded_rect, text…) y el nombre que le diste con named;
  • la línea del script que lo creó, con Open para ir a ella en tu editor de código y Copy para llevarte archivo:línea. Si lo creó una función tuya, debajo aparecen las líneas que la llamaron (called from main.py · line 11), cada una también se abre con un clic;
  • su posición, rotación (en radianes, como rotate_to, y en grados), escala, tamaño y centro en unidades de la escena; su relleno y su trazo con el color en hexadecimal, y su opacidad;
  • su z_index, su capa, su profundidad de parallax o si es HUD, si es visible, el texto que dibuja y cuántos hijos tiene.

Sin nada seleccionado, el inspector muestra la cámara de la escena: su posición, rotación, zoom (o campo de visión, recorte y objetivo en perspectiva), el encuadre y, si está activa, la profundidad de campo. Los valores son los del instante de la línea de tiempo, así que cambian al reproducir o recorrer la escena. Un clic sobre una letra selecciona el texto entero y, a igual z_index, gana la caja más pequeña bajo el cursor: la etiqueta sobre su tarjeta, el miembro dentro de su grupo. Inside … sube al grupo que lo contiene. Las líneas siguen siendo correctas tras cada recarga, aunque la recarga reutilice segmentos ya compilados.

La Console (J, o el botón de la barra) reúne lo que antes solo llegaba a la terminal: los avisos de Gaanim y de Python (warnings.warn), lo que escribe print, los errores del script con su traceback y las líneas de estado de cada recarga. Cada mensaje ocupa una fila y enlaza con la línea del script que lo produjo; un clic en la fila muestra debajo el mensaje completo y, si lo tiene, su traceback. Filtra por errores, avisos y mensajes, busca por texto o archivo, copia lo listado como en la terminal y limpia la lista. Por defecto muestra solo la última ejecución del script; History conserva las anteriores. Mientras está oculta, una insignia arriba a la izquierda cuenta los errores y avisos de la última ejecución y la abre con un clic.

Animations (T, o el botón de la barra) abre sobre la barra de reproducción una línea de tiempo de la escena: una fila por objeto animado, más la de la cámara, y un bloque por cada play que lo anima, coloreado por tipo (movimiento, transformación, aparición, estilo, trazo o cámara). Las animaciones de las letras de un texto aparecen en la fila del texto. Muestra el segmento bajo el cabezal; Whole scene muestra toda la escena, y la búsqueda filtra por objeto o por animación. Un clic o un arrastre en la regla mueve el cabezal. Un clic en un bloque pausa, lleva el cabezal a su inicio y selecciona el objeto para verlo en el inspector; un doble clic abre la línea del play. Al pasar el cursor, el bloque muestra su duración, su curva, la línea del play y las líneas desde las que se llamó. Un clic en el nombre de la fila selecciona el objeto y un doble clic abre la línea que lo creó. Mientras está oculto, el panel no cuesta nada por fotograma.

El inspector aparece arriba a la derecha y el panel de animaciones sobre la barra de reproducción. Arrastra cualquiera de los dos por su cabecera (la fila con los puntos) para llevarlo a otro sitio de la ventana; se queda ahí al cambiar de selección y nunca sale de la pantalla. Un doble clic en la cabecera lo devuelve a su sitio. Mientras el panel de animaciones está en su sitio, la consola se coloca encima de él.

Open usa el comando de GAANIM_EDITOR (por ejemplo code --goto {file}:{line}); sin ella, el primero que encuentre en el PATH entre VS Code (code), Cursor, VSCodium, Zed y Sublime Text, y si no hay ninguno abre el archivo con la aplicación predeterminada, que no salta a la línea. El panel de error del script también ofrece Open in the editor.

Recorte y máscaras

Cualquier objeto vectorial puede recortar a otro con una máscara viva que se recalcula en cada fotograma: consulta Drawable.clip y Drawable.no_clip.