The scenes

A hundred and thirty-seven, in four categories. Three come from jasalt/jolt-android-experiment, identical apart from namespace names and whitespace, and the other hundred and thirty-four are ports from raylib-jolt-demo (originally raylib-jlt).

Frame rates are measured on an iPhone 17 Pro running iOS 26.6.1, with the probe seam described in a REPL on the phone. Anything at 58 or 59 is vsync-limited and has headroom; the numbers below 58 are the ones that were tuned to get there. Every scene has a reading. The ten texture scenes read 58 or 59 and are recorded as 58, the median of their readings. The five render-target scenes (Render Texture, Framebuffer Rendering, Mouse Painting, Magnifying Glass and Top Down Lights) read 58 on the final build, the one without depth buffers on the 2D passes. A texture scene pauses on its first open after launch while its pixels are computed and uploaded, from about 0.17 s to about 1.0 s for six of the ten, and a reopen costs about one frame for a texture the scene keeps; the rows give each figure. A render-target scene pauses for 29 to 33 ms on a first open, except Top Down Lights at 75 ms and Render Texture at 103 ms (it was the first scene opened after launch, so part of that is the launch). These are phone measurements of the largest single frame. One drops below the range in its heaviest view: 3D Split Screen reads 58 idle and while a player walks through the trees, but 31 while a player walks out of the grove and looks back at all of it.

Generative

sceneper framefpsnotes
Spirograph~1000 lines55draws itself, then resets
Kaleidoscope708 lines5860-point trail, twelve-fold symmetry
Fireworks~120 circles58seeded, so it replays identically
Penrose340 triangles, ~2400 calls57P3 tiling by deflation
Fourier Epicycles~200 lines59a square wave, drawn by circles
Flow Field90 particles, 8-point trails54the first port the budget bit
Lorenz450 lines, re-projected each frame58see the sweep in performance
Life1470 rectangles falling to ~67059reseeds itself when the board stalls
Bullet Spiral~350 circles59the count is bounded by how fast a bullet leaves

Fractals

sceneper framefpsnotes
Hilbert Curve1023 lines60order 5, drawn progressively
Fractal Tree511 lines59sways; the only light-background scene
L-system Plant1488 segments59static once grown, which is why it is cheap
Automata2551 rectangles58scrolls; rules cycle on a timer

Toys

sceneper framefpsnotes
Following Eyes6 circles59identical to the Android experiment apart from names and whitespace
Touch Trail~40 circles59identical to the Android original apart from names and whitespace; the first scene here that wanted a finger
Boids2025 distance tests, 90 draws52the cheapest drawing and the dearest thinking
Double Pendulum~200 trail points59chaotic, so it never repeats
Starfield~300 points58seeded
Tesseract32 lines59the cheapest scene here, and a useful control
Colour Wheel540 vertices59rlgl immediate mode
Sine & Cosine~600 lines58the projections, and their traces
Clock42 rectangles59libc time() through the FFI
Pie Chart186 vertices58segments sized per wedge
raylib Logo4 rectangles and a word58nothing eased, on purpose
Easings405 lines and 15 dots58all fifteen curves on one clock
Angles13 lines and a circle59the two lines every circular scene here uses
Writinga few lines of text58the only scene that animates text
Ball Physics9 circles58restarts when everything settles
Random Sequence24 rectangles57a permutation, not independent draws
Collision Area3 rectangles59follows a finger, and drifts without one
Multitoucha circle and trail per finger58every point, not just point zero
Analog Clocka bezel, 60 ticks, 3 hands59rlgl stands in for the by-value Vector2 calls
Clock of Clocks144 bezels, 288 hands59the time spelled by little clock faces
Circle Sectorone sector, 1-36 segments58raylib's own segment floor, demonstrated
Colours25 swatches58every colour raylib names
Gradients4 quads, 8 vertices58one rlgl quad covers raylib's three calls
Ring Drawinga breathing annulus, stroked59the scene that would have caught the winding bug
Splines3 bases, 480 segments59Catmull-Rom passes through, the others do not
Rounded Rect2 rects, 4 quarter disks58DrawRectangleRounded, decomposed
Vector Angle2 arms and a filled arc58the angle is signed, and you can see the sign
Rounded Bars5 fans, 200 triangles60per-side rounding and a gradient, from one loop
Bezier48 segments and a control polygon58follows a finger; handles are derived, not dragged
Line Widths16 spokes, 16 widths58a direct test of thick-line winding
Scissor435 rectangles, most clipped58a scene's own clip, intersected with the safe region
Resizea rectangle and a corner handle58a sticky grab, because a finger has no hover
Text Alignment3 boxes, one word58MeasureText, the rare call with nothing to work around
Dashed Line~24 short lines58equal dashes, by walking the unit vector
Delta Time2 rectangles, 2 labels and an fps line58per-frame against delta time; at 58 fps the delta box already gains about 9 px a second
Random Valuesa number and a label58seeded, so it replays
Formatted Text2 lines of text58zero-padded score and MM:SS, padded by a private zero-pad built from str
Triangle Strip32 triangles58two per band, through draw-triangle so winding cannot cull them
Touch Ball1 circle and a caption58follows a finger; green while held, and it stays put when the finger lifts
rlgl Triangle3 vertices, 3 handles, 2 buttons58a colour per vertex; sticky corner grab, and the keys became buttons
Particlesabout 190 circles for fire, 324 for smoke, and 65 to 290 for water depending on the finger's height, plus an info box59emits at the finger while it is down, up to a cap of 600; tapping the box cycles water, smoke and fire
Bouncing Ball1 circle and 1 or 2 lines of text58a tap pauses, in place of SPACE; speed and radius scale with the shorter side, and a rotation clamps the ball back inside
Virtual Controlsa D-pad of 4 circles and a ring, an A button, a square, 3 lines of text58every finger down is tested, so a direction and A work together, in place of the arrow keys and SPACE; motion is delta-time based as in the original, and a rotation starts over
Starfield Effectup to 350 circles or streaks and 3 lines of text52a vertical drag sets the speed in place of the mouse wheel and a tap toggles streaks in place of SPACE; motion is delta-time based as in the original, and a rotation needs no reset
Easings Box2 triangles and 1 line of text58a tap restarts, in place of SPACE; frame-locked as in the original, and the box grows to fill the region below Back
Easings Testbed69 lines (4 frame, 64 plot segments, 1 rail), a circle and 3 lines of text58a swipe changes the curve in place of LEFT and RIGHT, a tap replays in place of SPACE and a long press toggles the plot in place of D; frame-locked as in the original
Rectangle Bounds4 lines, 2 rectangles and 3 or more lines of text, depending on the box58dragging the corner handle resizes the box and a tap on the wrap button toggles word or character wrap, in place of the mouse and SPACE; the layout is cached, so an idle frame measures nothing
rlgl Hue Wheela fan of 3 to 128 triangles with a colour on every vertex, or 2 lines a wedge as a wireframe, and 3 lines of text58a vertical swipe doubles or halves the triangle count in place of the mouse wheel, a horizontal drag sets the centre brightness in place of the arrow keys and a tap toggles the wireframe in place of SPACE, and the wireframe spokes ignore the brightness because a thick line takes one colour; the wheel fits the safe region, so UP and DOWN resizing is dropped; a different example from Colour Wheel
Still Logo2 rectangles and 1 line of text58nothing moves and nothing is read, so the logo is the finished picture where raylib Logo is the assembly; the label is placed by the measured text width, and the logo is sized to fit below Back
Font Sizes6 lines of text at 5 sizes and 6 colours58nothing moves and nothing is read; sizes are the original's 10, 20, 24, 30 and 40 scaled by one factor to fit below Back and rounded to whole numbers, and two lines are centred by the measured width; it is about size where Text Alignment is about placement
Inline Styling5 lines of text in runs, a rectangle behind some runs, a box of 4 lines and 1 legend line58nothing is read; the colours come from a tiny markup inside each string, [cRRGGBBAA] for the text, [bRRGGBBAA] for the background and [r] to reset, with a tag's alpha multiplied by the base's, and a bad tag prints as text; the CREATIVE word takes a new colour every 20 frames, frame-locked as in the original, from the project's seeded generator; lines are scaled by one factor and each run starts where the last ends by the measured width
Outline Thickness7 lines of text, 3 filled shapes, 8 lines (4 rectangle sides and 4 rounded sides), 4 corner arcs and 1 ring58the value sweeps on its own as the original does, until a touch takes over, and a vertical drag then sets it in place of UP and DOWN, the anchor taken on press and the release swipe ignored; below zero the plain rectangle draws nothing, the rounded one is a one pixel hairline and only the ring grows outward, as raylib does; the range is the original's -30 to 30 and the shapes are scaled to fit below Back
Basic Shapes1 line of text, 2 rectangles, 2 circles, an ellipse of up to 180 triangles, 1 line, 1 triangle and 4 outline lines58nothing moves and nothing is read; the ellipse is a triangle fan whose segment count follows its radius, the circle outline is a ring and the lines are thicker than the original's hairlines so they show on a phone; laid out for portrait, where the original is 800 by 450
Ellipse Collision2 ellipses of up to 180 triangles each, 2 outlines of as many lines, 2 centre dots and 3 lines of text59one ellipse follows your finger while it is down and stays where it was on lift; both turn red when they overlap, by the original's test, which samples 64 points round each rim and so can miss a very thin lens; a tap that starts inside the other ellipse hands the steering to it, and that touch does not drag the steered one, so a swap never moves it; a touch under Back is ignored; both sizes share one scale and the centres are clamped to stay on screen below the text
Screen Manager1 line of text for the screen and 1 for the hint, on a flat colour58a tap steps LOGO, TITLE, GAMEPLAY, ENDING in place of ENTER, and the timer steps every 90th frame as the original does, frame-locked and counted from the start so a tap does not postpone it; ENDING goes back to LOGO; a tap and the timer on one frame step once, and a finger down when the timer fires is dropped; a touch under Back is ignored
Basic Window1 line of text on a flat colour58nothing moves and nothing is read, so there is no control to replace; the line is centred by the measured text width and sits the original's 200/450 of the way down the field below Back, size and colour depart from the original for legibility: the line is fitted by the measured text width to about 85 percent of the screen width, where the original's size 20 came out near 10 pt on the phone, and it is DARKGRAY in place of LIGHTGRAY
Keyboard Ball1 circle, 1 line of text, and a thumb-stick ring and knob while held58a relative thumb-stick moves the ball, in place of the arrow keys: the press point is its centre, a finger within the tap slop of it does nothing, and beyond it the ball goes that way at the original's 2 pixels a frame, frame-locked like it; the vector is normalised, so a diagonal is no faster where the original's keys make it 2.83, and the ball is held wholly inside the field, which starts below the caption so the ball never covers it, and below Back where the original lets it leave the window; the speed and radius are scaled by one factor, and a rotation pulls the ball back in
Mouse Wheel1 square and 1 line of text58a vertical drag moves the box, in place of the mouse wheel: it follows the finger one for one from where the press anchored it, so the 20 pixel notch is whatever the finger travels, and the release swipe is ignored so the box does not move twice; the box is the original's 80 scaled by one factor, centred across the width and held inside the field below Back as the original holds it inside the window, and a rotation pulls it back in
Undo Redo1 line of text, up to 26 trail squares, 45 grid lines, 1 square, 1 line of text for the history, up to 26 history slots and 2 labelled buttons58a swipe moves the square one cell in place of the arrow keys, a tap on the square or the field recolours it in place of SPACE, and two buttons along the bottom replace CTRL-Z and CTRL-Y; the grid is the original's 30 by 13 and is not turned with the screen, the history holds the original's 26 states with redo cleared by a new action, and it is sampled every second frame as the original does; the history count sits above its strip instead of beside it, the grid lines are thicker so they show on a phone, and a button greys out when it has nothing to do; a touch under Back is ignored
Strings Managementup to 100 bordered text particles, each a border, a fill and 1 line of text, plus 2 lines of text and 3 labelled buttons58dragging a particle and lifting throws it in place of the left button, with the velocity of the last four :down frames; a long press cuts it in half in place of right-click; the shatter button arms the next tap to shatter it into characters in place of SHIFT and right-click; the shake button replaces middle-click; releasing a dragged particle over another glues them in place of CTRL while dragging, and only a drag past the tap slop that is set down slowly glues, so a throw across another flies on; the case button steps the six case transforms in place of keys 1 to 6, starting at UPPER because the opening sentence is already plain; typing a character to split the lone sentence is dropped, since there is no keyboard; physics use :delta-seconds with the original's per-frame friction, and the opening sentence is centred, with the text size fitted to the widest of the six case versions; a touch under Back is ignored
2D Camerathe original's 30-building skyline, the ground and the player box through a camera, a screen-space centre line, 1 line of text and 1 labelled button58a one-finger drag is a relative thumb-stick on x only (the press point is the centre, a dead zone of the tap slop) that moves the player at the original's 4 units a frame in place of the arrow keys; a two-finger pinch zooms between 0.25 and 3.0 in place of the wheel and a twist rotates in place of A and D, both about the field's centre; the reset button below Back replaces R and leaves the player where it is; a second finger ends the stick, and the finger left after a lift starts none until it lands again; the original's 800x450 view is fitted into the field below the button by a base zoom and the world is clipped to that field; a touch under Back is ignored
2D Camera Zooma 22 by 22 line grid (the original's 21x21 cells), 1 square, 1 circle, 1 box and the text "world origin" through a camera, a screen-space crosshair and 2 lines of text58a one-finger drag pans by the finger's movement over the zoom in place of the left-drag, so the world point under the finger stays under it; a two-finger pinch zooms between 0.125 and 64 in log space about its midpoint in place of the wheel and the right-drag, and the midpoint drags the world if it drifts; keys 1 and 2 are dropped, so the original's mode and its HUD word are gone, and the pinch's twist is ignored; the crosshair sits at the last touch or the pinch midpoint; a second finger ends the pan, a pinch acts only while exactly two fingers stay down, so a third finger or a lift pauses it and moves nothing, and the finger left after a lift pans nothing until it lands again; the world origin starts at the field's centre, not the top-left, because that is under Back; a touch that begins under Back pans nothing
2D Platformerthe 3 platforms, the floor and the sky of the original's 1000 by 600 map, the 40 by 40 player, 1 line of text for the camera mode and 5 labelled buttons59five buttons along the bottom, read from every touch point so two can be held at once, replace the keys: < and > walk in place of the arrows (the default font has no triangles), jump replaces SPACE and is held as the original holds it, camera steps the five modes on a press in place of C, and reset replaces R on a press and keeps the mode; the original's key-help line is dropped for a line naming the mode; physics, platforms and the five modes (centre, clamped, smooth, even-out, push) are the original's, with :delta-seconds for get-frame-time and no clamp (a 0.1 s stall still lands); the 800x450 view is fitted into the field by a base zoom and every mode's width and height become the field's, so the clamp and the push margins act on the phone's pixels; a touch under Back is ignored
2D Split Screenthe original's 20 by 11 grid of 40-unit cells with 220 [i,j] labels, a red and a blue player and a banner with 1 line of text in each of 2 scissored halves, and a divider58a relative thumb-stick in each half, read from :touch-points and assigned by the half a point lies in, steers that half's player at the original's 3 units a frame, normalised, in place of W, A, S, D and the arrows; a stick starts on the frame a point first appears in its half, with its centre there, so a tap never moves a player; two thumbs work at once; with two points in one half the stick follows the one nearer its finger; a thumb sliding across the divider ends the stick it left and starts a fresh one in the other half; the render textures become a scissor and a camera per half, stacked in portrait and side by side in landscape, with the camera's offset at the half's centre and a base zoom fitting the original's 400 by 440 half; the banners read "drag to move" in place of the key names and the labels keep the original's size 10, scaled with the world; a touch under Back is ignored
Input Gesturesa log of up to 20 gesture names in alternating rows, the newest in maroon, a test box with a title and a hint line, and a finger circle while a gesture is set58raylib's own gesture recogniser, read through GetGestureDetected as :raylib-gesture, names tap, double-tap, hold, drag and the four swipes, logged when the code changes and the finger is in the box; the finger comes from :pointer, the last position seen on a press or a drag when the gesture is reported on release, never the release position; pinch in and pinch out never appear, because raylib's SDL platform feeds its gesture system one touch point at a time, and the hint line says so; the log stacks above the box in portrait and runs down its left in landscape, with text sizes shrunk until 20 rows fit
Helitorusa helix wound round a torus and swept into a lit tube of 64 rings of 12 points (up to 900), hidden faces removed by hand, 3 lines of text (fps, compute and draw milliseconds; windings and detail; a hint) and 4 labelled buttons58a one-finger drag turns it by the original's 0.008 radians a pixel, with its velocity memory and coast to the idle turn, in place of the mouse drag; a two-finger pinch zooms between 110 and 520 in place of the wheel, and its midpoint and twist are ignored; four buttons below the field replace the arrow keys, read from every touch point so a button and the field work at once: windings - and windings + change the windings from 3 to 24 by one on a press (LEFT and RIGHT), detail - and detail + change the rings from 60 to 900 by 4 a frame while held (DOWN and UP); the original's key-help line is dropped for a hint line and its thousand-vertices-a-second figure is dropped from the HUD; the centre and zoom scale come from the field, not the original's 1000 by 560 window; a finger on a button or under Back turns and zooms nothing; the original starts at 260 rings and this starts at 64, chosen from the phone measuring 19 fps at 260, where 64 holds 60 fps (detail + still reaches 260); each visible quad is sent in the winding rlgl keeps, because the batch is drawn at a later flush with culling on
Rotating Cubethe original's grid of 10 and a red cube of side 2 shaded face by face, projected in software (about 6 triangles and 22 lines), and 1 line of text59no input, as the original has none; the cube turns one degree a frame about x and 0.7 of that about y, composed in the order of the original's two rlRotatef calls; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the grid is drawn under every face, so where the original's grid crosses the lower half of its cube (centred on y = 0, half below the grid) the lines a depth buffer would show in front of it are hidden here; the 3D view is the field below the caption, which is below Back, and is clipped to it
3D Camerathe original's grid of 20 and a red cube shaded face by face, projected in software (about 4 triangles and 42 lines), and 1 line of text58no input, as the original has none; the camera orbits at radius 12 and height 8 by 0.02 radians a frame, looking at (0, 1, 0), with the original's fovy 45, widened in a field narrower than 800x450 so the original's horizontal view still fits; the 3D view is the field below the caption, which is below Back, and is clipped to it
Orthographic Projectionthe original's grid of 10 and three cubes (red, green, blue) shaded face by face, projected in software (about 18 triangles and 22 lines), and 1 line of text58a tap anywhere outside Back toggles perspective (fovy 45) and orthographic (fovy 12) in place of SPACE, and the caption says "tap" for "SPACE"; a touch that begins under Back toggles nothing; in a field narrower than 800x450 the perspective fovy is widened and the orthographic height scaled so the original's horizontal view still fits; the 3D view is the field below the caption, which is below Back, and is clipped to it
Spinning Cubesthe original's grid of 12 and five cubes of side 1 (red, orange, green, blue, violet) shaded face by face, projected in software (30 triangles and 26 lines), and 1 line of text58no input, as the original has none; each cube turns 2 degrees a frame about the axis (0.3, 1, 0), 30 degrees ahead of the one before, after a translate to its place, composed in the order of the original's rlTranslatef and rlRotatef calls; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the 3D view is the field below the caption, which is below Back, and is clipped to it
World to Screenthe original's grid of 10 and a red cube of side 2 shaded face by face, projected in software (about 6 triangles and 22 lines), a label "Enemy: 100/100" pinned above the cube, a position readout and 1 line of caption58no input, as the original has none; the camera orbits at 0.3 radians a second from :delta-seconds, where the original uses get-frame-time, and the label sits at the world point (0, 2.5, 0) projected through the same camera the cube is drawn with, truncated to whole pixels and centred by the measured text width; the readout is drawn over the top left of the 3D view as in the original and the caption sits below Back, and all text uses the field's text size where the original uses 20; the original's fovy 45 is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the grid is drawn under every face, so where the original's grid crosses the lower half of its cube (centred on y = 0, half below the grid) the lines a depth buffer would show in front of it are hidden here; the 3D view is the field below the caption, which is below Back, and is clipped to it
Wireframe Shapesthe original's four wireframe solids, a pyramid (8 edges), an octahedron (12), a torus (196) and a helix (64), drawn as 280 lines projected in software, on the original's dark background, and 1 line of text59no input, as the original has none; each shape stands at x = -6, -2, 2 or 6 and tumbles 0.9 degrees a frame about the axis (0.4, 1, 0.3), after a translate to its place, composed in the order of the original's rlTranslatef and rlRotatef calls; edges are built once and only transformed and projected per frame; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; a line crossing the near plane is clipped to it; the 3D view is the field below the caption, which is below Back, and is clipped to it
3D Free Camerathe original's red cube of side 2 drawn flat as DrawCube does it, its maroon wires, a grid of 10 and the translucent help box with four lines of text, projected in software (6 triangles and about 31 lines from the start), and a "reset" button58raylib's own UpdateCamera(CAMERA_FREE) is rebuilt in pure Clojure from rcamera.h (forward, up, right, move forward, move right, move to target, yaw, pitch with the view locked 0.001 radians short of vertical); a drag that starts in the upper two thirds of the field looks, at 0.003 radians a pixel scaled by 800 over the field's width; a relative thumb-stick started in the lower third replaces WASD and moves along the view at 5.4 units a second times the frame time, one speed in every direction, where the original's keys add up on a diagonal; two fingers both in the upper two thirds pinch to dolly in place of the wheel, and two fingers in different regions are look plus stick; "reset" replaces Z and sets only the target to the origin; the cursor lock, the arrow keys, Q and E, Space and Control, the middle-mouse pan, the gamepad and the keypad keys are dropped; the help text names the touch controls; a face with a corner behind the near plane is dropped whole, so flying into the cube makes its faces vanish; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the grid is drawn under every face, so where the original's grid crosses the lower half of its cube (centred on y = 0, half below the grid) the lines a depth buffer would show in front of it are hidden here; the 3D view is the field below the caption row, which is below Back, and is clipped to it
Yaw Pitch Rollthe original's plane of five boxes shaded face by face over a grid of 12 sunk to y = -3, projected in software (44 triangles and 26 lines, because the wing and the tailplane are cut into outer panels, with a strip for the tailplane's overhang, so that the painter's sort orders them against the fuselage), its title, its three gauges (label, centre-out bar, tick and angle in degrees) over a dark panel with the line "let go and each axis eases back to level", and two buttons58A, D, W and S are a relative thumb-stick started in the 3D area: past gesture/slop left is A (yaw +1.1 degrees a frame), right is D (-1.1), up is W (pitch +0.9) and down is S (-0.9), each axis on its own so a diagonal turns both as two keys do; Q and E are two buttons read from :touch-points, so a thumb on the stick and a thumb on a button act together: "roll right" is Q (-1.3 a frame, tested first as in the original) and "roll left" is E (+1.3), named by what the plane does; each angle is held to 90 and an undriven one eases by 0.94 a frame, to 0 under 0.15 degrees, per update like the original; the rotations are composed in the order of the original's rlRotatef calls (yaw, pitch, roll, so a vertex rolls first); the readout is %6.1f deg without the padding, the three gauges stand side by side instead of stacked and are labelled yaw, pitch and roll without the key names; a face with a corner behind the near plane is dropped whole; the original's 45 degree fovy is kept in a 3D area as wide as 800x450 and widened in a narrower one; the 3D area is the field below the title, which is below Back, down to the panel
Box Collisionsthe original's grid of 20, five static boxes standing on it and the lime player cube of side 2, shaded face by face and projected in software (36 triangles and 42 lines from the start), and 1 line of caption58A, D, W and S are a relative thumb-stick started in the 3D area: past gesture/slop left is A (x - 0.18 a frame), right is D (x + 0.18), up the glass is W (z - 0.18, forward) and down is S (z + 0.18), each axis on its own, but a diagonal scales both by 1/sqrt 2 so it moves 0.18 in all, where the original's two keys move 0.18 on both (about 1.41 times as fast), the one deliberate difference; the speed is per update like the original and the player is not held to the grid, as in the original; a box turns red while the 3D AABB overlap test \
3D Pickingthe original's gray cube of side 2 with its DARKGRAY wires, a grid of 10 and the pick ray, projected in software (4 triangles and 29 lines from the start), and the readout "BOX SELECTED" with the hit's distance, point and normal58a tap replaces the click: it casts GetScreenToWorldRay through the tap's press position and tests the cube with GetRayCollisionBox, through the same camera and viewport the frame is drawn with; a hit turns the cube RED with MAROON wires and adds the 0.2 larger GREEN wires, and a tap while selected lets go without casting, as the original's latch does; the ray stays drawn, 10000 units from the camera as DrawRay does, with the stretch inside the cube cut out (there is no depth buffer, so a line through the opaque cube would show across it), though a part of the ray behind the cube can still show through it from some angles; it is seen end-on (a point) from the camera that cast it until the camera moves; a tap that misses says "missed" in the readout; the original's fps counter is dropped; a drag never picks; the camera circles by itself, 0.005 radians an update, at height 10 and radius 14 round (0, 1, 0), and stands still while a finger is down; a one-finger drag replaces the mouse-look and orbits it, 0.004 radians a pixel times 800 over the field's width, the height following the vertical drag between 2 and 25; the cursor lock, its right-click toggle and its hint, and W, A, S and D are dropped; a face with a corner behind the near plane is dropped whole; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one; the 3D view is the field below the caption, which is below Back, and is clipped to it
Waving Cubes9 by 9 columns of width 1 and height 0.6 to 5.4 (the original has 14 by 14, 196), coloured by three sines and shaded face by face, projected in software (468 to 486 triangles, no lines), and 1 line of text59no input, as the original has none; each height is 0.6 + 2.4 (1 + sin(0.6 ix + 0.6 iz + t)) with t 0.06 a frame; the camera orbits 0.012 radians a frame at 1.3 times and 0.9 times the grid's span, looking at (0, 1.5, 0); the first version of this port, with all 196 columns through net.b12n.raylib-ios.soft3d/cube and finish, ran at 15 fps on an iPhone 17 Pro (67 ms a frame), and this one draws 81 columns, each a net.b12n.raylib-ios.soft3d/cube, painted far to near by axis order with no triangle sort; the original's fps counter is dropped; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; a little painter's residue can remain where a tall column stands before a short one; the 3D view is the field below the caption, which is below Back, and is clipped to it
Solar Systemthe original's gold Sun of side 3, blue Earth of side 1.4 and light gray Moon of side 0.7 shaded face by face, projected in software (14 to 18 triangles, no lines), and 1 line of text58no input, as the original has none; the Earth orbits half a degree a frame at 9 from the Sun and spins 1 degree a frame, the Moon orbits the Earth 2 degrees a frame at 2.6, composed in the order of the original's rlRotatef and rlTranslatef calls; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the 3D view is the field below the caption, which is below Back, and is clipped to it
Point Cloud400 points (the original has 1500), each a screen-space square of 2 triangles (800 triangles, no lines) in place of its small cube, coloured by position on black, and 1 line of text60no input, as the original has none; the points are the first 400 of the cloud the project's seeded LCG makes in place of GetRandomValue, and the cloud turns 0.3 degrees a frame about the y axis; the first version of this port, with all 1500 points as squares sorted by finish, ran at 9 fps on an iPhone 17 Pro (110 ms a frame), and this one draws 400; the squares are unshaded, painted far to near by 64 depth buckets, and the caption says "square" where the original says "cube"; the original's 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one so the original's horizontal view still fits; the 3D view is the field below the caption, which is below Back, and is clipped to it
First-Person Camerathe original's 40 random columns (x and z in -20 to 20, height 2 to 12, each colour 60 to 255) shaded face by face over a grid of 40, seen from an eye 2 up on a sky of (140, 190, 230), projected in software (about 70 triangles and 60 lines from the start), and 1 line of caption58the columns come from the project's LCG seeded with 20261002 in the original's order (x, z, h, r, g, b), so the yard is the same every run; the camera is the original's own yaw and pitch maths (it does not call UpdateCamera): forward on the ground is (cos yaw, sin yaw) and the target is the eye plus (cos pitch cos yaw, sin pitch, cos pitch sin yaw), with the Free Camera's stick direction reused; a relative thumb-stick started in the lower third of the field replaces WASD and walks 0.25 an update along the ground heading, one speed in every direction where the original's keys add up on a diagonal; a drag that starts in the upper two thirds replaces the mouse, 0.004 radians a pixel scaled by 800 over the field's width, the pitch held to +-1.4 radians as the original does; both work at once, each by its own finger (net.b12n.raylib-ios.stick), and a resting finger is never adopted; the frame counter (fps!) is dropped and the key text becomes a touch caption below Back; the original's 60 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one; a face with a corner behind the near plane is dropped whole; the grid is drawn under every face, so a column hides the grid under its foot, and overlapping columns are ordered whole, face by face; the 3D view is the field below the caption, which is below Back, and is clipped to it
First-Person Mazethe original's 16 by 16 maze as 137 wall cubes (CELL 4 by 3 by 4, a checker of (120, 130, 160) and (95, 105, 135) shaded face by face) over a grid of 40 slices of 4, on a clear colour of (16, 18, 26), from an eye 1.6 up at fovy 68, projected in software, with the original's minimap (a translucent black panel, a rect for every wall, a red circle for the player and a gold line along the heading) in the top right of the 3D field, and 1 line of caption59the maze, wall cubes, per-axis collision with a box of radius 0.9 (x first, so a diagonal into a wall slides), 5 * dt speed and the heading maths (target = pos + (sin h, cos h), no UpdateCamera) are the original's, and the player starts at (6, 6) facing +z; a relative thumb-stick started in the lower third of the field replaces W, S, A and D at one speed in every direction where the original's keys add up on a diagonal, and a drag that starts in the upper two thirds replaces LEFT and RIGHT, turning 0.004 radians a pixel scaled by 800 over the field's width (a rate borrowed from First-Person Camera, as a key has none; vertical motion turns nothing as the original has no pitch); both work at once, each by its own finger (net.b12n.raylib-ios.stick), and a resting finger is never adopted; the original's D adds +x at heading 0, which is the left of the glass, so the stick maps by what shows on the glass and pushed right it strafes right; walls behind the eye or outside the sides of the view are not built, nor are walls that rays across the maze grid cannot reach because other walls stand in front (checked over 35,712 poses to hide only what is hidden), which leaves the picture unchanged; the minimap's cell is sized to the field (at most 0.3 of its width and 0.4 of its height) and its 4, 12 and 1 scale with it; the title and key text are dropped and the key text becomes a touch caption below Back; a face with a corner behind the near plane is dropped whole, the grid is drawn under every face, and adjacent walls are ordered whole, face by face
3D Split Screenthe original's 121 trees (a lime cube of side 1 on a brown post of 0.25 by 1 by 0.25, 4 apart) and two cube players on a beige plane, flat coloured as DrawCube colours them, seen by two software-projected cameras in two scissored halves on a sky of SKYBLUE (at the start on the phone's screen a half builds 126 and 197 triangles, no lines, 323 for both halves), each with a translucent bar and 1 line of text, and a divider58the world, both cameras (player one at (0, 1, z1) looking down +z, player two at (x2, 3, 0) looking down +x, fovy 45), the start of -3 and the speed of 10 * dt are the original's; the halves stack in portrait and sit side by side in landscape in place of the two render textures, each with its own camera and scissor, and a half narrower than the original's 400 by 450 gets a wider fovy so its horizontal view still fits; a relative thumb-stick in each half replaces W and S and UP and DOWN, one net.b12n.raylib-ios.stick per half, so two thumbs work at once, a stick starts only on a fresh press inside its own half and follows only that finger by its touch id, and a resting finger is never adopted; a stick reads only up or down from where the thumb began, past the tap slop, at the original's one speed (the keys move along one axis); the key text in the labels becomes the touch controls; the plane is cut to the part ahead of the eye, from half the depth at which the bottom of the glass meets the ground (out of sight), because a quad with a corner behind the near plane is dropped whole, so the picture is the original's; a cube with a corner behind the near plane is dropped whole, so a tree you walk into vanishes instead of being clipped; the plane is painted first, and the cubes are painted whole, far to near by the distance of their centres from the eye, without finish, so where two overlap a face can paint over one a depth buffer would put in front; trees wholly behind the eye or outside the sides of the view are not built, and nor are cube triangles wholly outside the half's rectangle (more than a pixel past one side), which leaves the picture unchanged; each half's 3D view is clipped to it; on the phone it reads 58 idle and while a player walks through the trees, and 31 while a player walks out of the grove looking back at all of it, since every tree is then in view
Bouncing Spheresthe original's 6 balls (radius 0.3 to 0.6, the palette RED, ORANGE, GREEN, SKYBLUE, VIOLET, GOLD) under gravity 0.01 with wall restitution 0.9 in a box of plus or minus 4, over a grid of 10, projected in software (about 240 triangles and 22 lines a frame), tessellated 6 rings by 8 slices where the original's sphere! is 10 by 14, because 10 by 14 built in 0.89 ms a frame on the laptop and a scene is sized to about 0.30 ms on the laptop (see "Sizing a scene on the laptop" in the performance guide), and 6 by 8 builds in about 0.39, inside the edge of that rule (60 fps on the phone); 1 line of text60the balls, their physics (one step a frame, with no frame time, as the original), colours (from the LCG, for GetRandomValue, seeded 20261002), the camera at (10, 8, 10) and the grid are the original's; a tap outside Back replaces SPACE and respawns all six, from where the LCG stopped; balls that touch no other are painted whole, far to near by the distance of their centres from the eye, without finish; balls whose spheres intersect (the original has no collision, so about 4 frames in 9 once they settle) are grouped transitively and their triangles sorted together by finish, placed among the rest by the group's mean centre, so a little residue by mean depth can remain where they cross; a lone ball whose depth falls between the members of a group can also paint over the nearer member (5 of 12,000 simulated poses); and the grid is drawn first so a line under a ball is lost; the caption's "SPACE respawns" becomes "tap respawns"; the 3D view is clipped to the field
Bunnymarkthe original's 200 opening bunnies, as tinted squares of the sprite's size (32 units, scaled by the width over 800), added 60 a frame while a finger is held up to a cap of 40000 (a hold from 200 ends at 40040, as the original's does), each bouncing off the four edges at its own velocity and tint, with a header bar showing the count, the host's live fps and 1 line of hint, and a clear button; one draw-rectangle per bunny and nothing else, so it is a stress test by design: it builds in about 0.24 microseconds a bunny under jolt on the laptop (0.025 ms at 100, 0.24 ms at 1000, 9.7 ms at 40040) plus one FFI call each, and the 0.30 ms target is passed at about 1,250 bunnies, fewer with the draw calls58the bunny physics (step: move, then flip the velocity of an axis whose sprite is outside the window, with no clamp), the spawn (x and y at the touch, vx and vy GetRandomValue(-250, 250) / 60, each channel 90 to 255, drawn in that order from the LCG seeded 20261002 in place of GetRandomValue), the opening scatter, the batch of 60, the cap and the order of a frame (clear, else spawn, then step all) are the original's; holding a finger inside the field and outside Back and the clear button replaces the left mouse button, so nothing spawns above the field or off its edge, a finger that began on the clear button or in Back never spawns, and one that began in the field stops spawning while it is over the button; a tap that begins on the clear button replaces SPACE; when the phone turns, every bunny is clamped into the new field and the gesture is reset, where the original's window never changes; the sprite's rabbit silhouette and its alpha are dropped, so a bunny is a flat square and one over another hides it instead of showing the tint of the one beneath through a transparent ear; the field is 800 units wide and as tall as the phone needs below Back (about 1469 at 1206 by 2334) where the original's is 450, so the velocities, which are per frame, take longer to cross it, and the bunnies are square rather than stretched; the fps is the host's GetFPS, read every frame as Starfield and Delta Time do; the bunnies are held in arrays that advance updates in place, which keeps the per-bunny path free of allocation; the original's mouse hint text becomes "hold to add - tap clear"
Background Scrollingthe original's three skyline layers (back, middle, front) on a clear colour of (5, 44, 70), each a row of building rects (25, 16 and 9 buildings in columns of 32, 52 and 88 units, so the buildings are 28, 46 and 76 wide after the 15 percent gaps, in (20, 52, 78), (12, 34, 50) and (4, 12, 20)) drawn twice, one period apart, standing on the bottom of the screen, and the red caption; 50 rects and 1 line of text a frame58the layers' seeds, horizons, column widths and colours, the building heights (hash01 of the seed plus the column), the speeds of 0.1, 0.5 and 1.0 units a frame to the left with no frame time, and the wrap at minus 800 are the original's; the three texture-from-fn skylines become one rect per building from the same arithmetic (a test samples every 11th column and every 9th row, at six offsets, against a transcription of the original's skyline-pixel), so the transparent sky and the gaps show the layer behind as before; the 800 units of a period are one screen width, so the picture is scaled by the width over 800 and sits on the bottom of the screen, with more sky above it on a portrait phone; a rect wholly off the screen is not drawn; there is no input, as in the original; it builds in about 0.03 ms under jolt on the laptop plus 50 FFI calls, against the 0.30 ms target
Sprite Stackingthe original's 40 slices of a small car, drawn last row first so the roof lands on top, each a stack of rects and circles (two wheels, then the body, the shoulders, the cabin and its glass, with rounded corners of radius 3 or 2) turned about its own centre by one rotation and set spacing higher than the slice below, on RAYWHITE, with 3 readouts under Back and a note at the bottom; 92 rects, 164 circles and 160 matrix calls a frame58the slices (every image-draw-rectangle! and image-draw-circle! of draw-layer! and slab!, by the same thresholds on i / 39), the 3x scale, the 40 layers, the spacing of 3.2 held to 0 to 5, the spin of 30 degrees a second that the keys change by 0.35 a frame, the order of drawing and the rotation about each slice's centre are the original's; each slice is drawn as rects and circles under an rlgl rotation (push, translate, rotate, pop), so the 56 by 784 image and its textured quads are dropped and a corner is a true circle where the original's is a raster one; a horizontal drag replaces the arrow keys and A and D, as a relative stick from the press point that adds or takes off 0.35 of speed a frame past the tap slop, with no cap as in the original; a two-finger pinch replaces the mouse wheel, adding 2.5 times the change of distance minus one to the spacing (the original adds 0.1 a notch; this is a guess at the feel, and additive so that 0 can open again), held to 0 and 5, and only while exactly two fingers stay down; the stack is scaled by the smaller of the width over 800 and the field's height over 450 and centred on the field; the key hint becomes "drag: spin - pinch: spread the layers" and the note says the slices are rects and circles; it builds in about 0.08 ms under jolt on the laptop plus about 420 FFI calls, against the 0.30 ms target
Smooth Pixel-Perfectthe original's three spinning rects (20 by 20 black, 30 by 10 red and 15 by 25 blue, turning about their top-left corners by the spin, minus the spin and the spin plus 45) in a 160 by 90 world on RAYWHITE, swaying on the original's sine and cosine path, drawn at the largest whole number of screen pixels for a world pixel that fits the field (7 on a 1206 wide phone, so the window is 1120 by 630), with the camera on the integer part of the path and, with smoothing on, the remainder times that zoom slid off the offset, and the original's four readouts; each rect rasterised to the 160 by 90 grid as row runs of cells (at most 86 runs a frame, one draw-rectangle each), 1 fill and 1 camera, about 130 FFI calls a frame58the world, the sway (cx is sin t 50 - 10 and cy is cos t 30, truncated toward zero for the integer part), the three rects and their spins, the 60 degrees a second, the remainder times the ratio, the two blit rects (overscan: -R, -R, W + 2R, H + 2R of the window; off: centred) and the readouts are the original's; the S key becomes a "smooth" button and O an "overscan" button, side by side below Back, each flipped by a tap that began on it, and the key hint is dropped from the status line since the buttons say it; get-time and get-frame-time become the sum of :delta-seconds; the fps is the host's GetFPS, read every frame as Starfield shows it; the 160 by 90 render texture is dropped, so the picture is drawn under with-camera-2d and clipped by a scissor, and each rotated rect is rasterised on the CPU to the 160 by 90 grid (a cell is filled when its centre is inside the rect, a row of cells is one rect), which is what the nearest-filtered texture showed, so the slanted edges are chunky staircases (an edge tie goes by a half-open interval, not the GPU's rule); the overscan stretch is one zoom, the larger of the two the original uses across and down (5.0625 and 5.111 at 5), so the world always covers the window and no strip of the clear colour shows, and with overscan off the picture is at the zoom less one (4 at 5, the original's) instead of 4/5 of it; the original's window size is the window drawn here, 160 times the zoom by 90 times the zoom; builds in about 0.052 ms under jolt on the laptop plus about 130 FFI calls (about 0.11 ms in all), against the 0.30 ms target
Viewport Scalingthe original's game (a white clear with a LIME circle of radius 20, at 64x64, 256x240, 320x180 or 3840x2160), its six policies (KEEP_ASPECT_INTEGER, KEEP_HEIGHT_INTEGER, KEEP_WIDTH_INTEGER, KEEP_ASPECT, KEEP_HEIGHT, KEEP_WIDTH) and its six readouts (window, game, type, scale ratio, source size, destination size), in a virtual window on the field, with two pairs of < > buttons; 1 scissor, 1 push and scale, about 28 FFI calls a frame59the maths of compute-rects is the original's line for line (the quirks included: the two height policies are one function and so are the two width policies, so only KEEP_ASPECT_INTEGER snaps), and the blit rectangle, the scale ratio, the source and destination sizes and the mouse mapping (mx - dx) * sw / dw on both axes are the original's, with the scale readout INVALID under 0.001 as it is there; the resizable 800x450 window becomes a virtual window that starts in the original's 800x450 shape at 80 percent of what the field allows (so it can grow) and is resized by dragging a square handle in its bottom-right corner, clamped to the field; the mouse becomes the finger, which moves the circle while it is down and leaves it where it was after, starting in the middle; the < > pairs become taps that began on a button, wrapping; the render texture is dropped, so the destination is a scissor and the game is drawn under a push, translate and scale; the panel's box is dropped and the readouts sit above the field, with the game and type lines between their buttons, so the order is not the original's; a destination that is empty (an integer policy for a game bigger than the window, where quot gives 0) draws nothing and gives no mouse position; builds in about 0.010 ms under jolt on the laptop, against the 0.30 ms target
Window Letterboxthe original's 480 by 360 picture (a clear of (24, 28, 38), two rows of 12 alternating blue blocks, a GOLD circle swinging on 240 + 90 sin t, two lines of text and a RED crosshair) at the largest scale that fits a virtual window, centred, with black bars where it does not fill, and a readout of the window, the scale and the bars; 1 scissor, 1 push and scale, about 60 FFI calls a frame58fit, the blit rectangle (int ox) (int oy) (int (* 480 s)) (int (* 360 s)), the bars the readout reports (the offsets cut to whole pixels), the two-decimal scale and the mouse mapping (int (/ (- mouse offset) s)), truncating toward zero, with the crosshair drawn only over the picture, are the original's; the resizable window becomes a virtual window that starts in the original's 800x450 shape at 80 percent of what the field allows (so the bars are there from the first frame) and is resized by dragging a square handle in its bottom-right corner, clamped to the field; the mouse becomes the finger, which moves the crosshair while it is down and leaves it where it was after, starting in the middle; get-time becomes the sum of :delta-seconds; R, the window-state calls and the resizable flag are dropped (the window is always resizable, so the second text is the original's resizable branch, in GREEN); the render texture is dropped, so the blit is a scissor and the picture is drawn under a push, translate and scale; the strip along the window's bottom edge is dropped and its words sit in two lines above the field; builds in about 0.010 ms under jolt on the laptop, against the 0.30 ms target
Fog of Warthe original's 25 by 15 map of 32 unit tiles in two blues (BLUE and (0, 121, 241, 230), picked at random per tile) with outlines of (0, 82, 172, 128), the 16 unit RED player and a fog that is black where unexplored, black at alpha 163 where remembered and clear where lit, with 2 lines of text; 375 tiles, 80 outline strips, 1 player rect, one batch of up to 375 quads (2250 vertices) and 2 text lines a frame, about 3600 FFI calls at worst60the map, the tile size, the visibility of 2 (the loops run t-2 to t+1, four tiles a side, as there), tile-of, the age-then-light order, the fog alphas (unexplored 255, lit 0 and remembered 204 drawn into a texture cleared to clear black, which stores 204 * 204 / 255 = 163, so 163 here), SPEED 5, the clamp to 0 to 784 and 0 to 464, the patrol loop until the first move and the text are the original's; the 25 by 15 render texture stretched with a bilinear filter becomes one quad a tile in a single rlgl triangle batch (the vertex order of draw-gradient-quad, the colour set only when it changes, on the tile fills' own whole-pixel edges), each corner's alpha the mean of the four tiles that meet there (an edge tile repeats off the map, as the texture's clamp does), which is the same bilinear blend sampled half a tile over, so a clear patch looks about half a tile smaller at its edge; the arrow keys become a relative thumb-stick on net.b12n.raylib-ios.stick (an axis is on past the tap slop, a diagonal is scaled to the one speed where the original's adds 5 on both axes, a resting finger never steers); GetRandomValue is the LCG; the whole 800 by 480 map is drawn at the largest scale that fits below Back, so the bottom row the original's 450 high window cuts off is shown; the 375 outlines are 80 strips across the map; a tile whose four corners are clear is not drawn; the hint says "DRAG" where the original says "ARROW KEYS"; builds in about 0.30 ms under jolt on the laptop (update, corner alphas and the batch with the FFI stubbed out), at the 0.30 ms target, which held 59 to 60 fps on the phone
Blend Modesthe original's night skyline (a gradient from (18, 12, 42) to (58, 24, 74), 19 buildings in (14, 10, 22) and 81 lit windows) under three coloured glows (cyan, magenta and yellow, radius 70, 60 and 55 texture pixels) drawn through BLEND_ALPHA, BLEND_ADDITIVE, BLEND_MULTIPLIED and BLEND_ADD_COLORS in turn, with 2 lines of text; 1 gradient quad, 100 rects, 1 clear quad and 48 gradient triangles a frame, about 350 FFI calls58the four modes and their order, the 800 by 450 picture over 400 by 225 textures, hash01, the skyline's columns of 22, their tops and the window rule, the three blobs and the linear falloff are the original's; the two procedural textures are redrawn as shapes (the sky is one gradient quad and so is smooth where the original steps its channels by row, the buildings and windows are rects checked pixel for pixel against the original's rule, each glow is a fan of 16 triangles with a centre of (r, g, b, 255) and a clear rim, so the colour interpolates as k * colour); the glows are blended one at a time where the original sums them into the texture and blends once, so where two glows overlap the picture differs (under ADDITIVE about half as bright, ALPHA and MULTIPLIED too, ADD_COLORS the same), and a 16-sided fan is not a disc; under MULTIPLIED a source of (0, 0, 0, 0) leaves the pixel alone in glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA), so the original's note that it blackens the rest of the picture does not hold, and the clear quad it stands for changes nothing; SPACE becomes a tap anywhere outside Back, on its release; the two texts sit above the picture at a readable size rather than on it at size 10; the picture is the largest 16:9 that fits below them; BeginBlendMode and EndBlendMode are the first blend bindings and the draw ends the mode in a finally; the GLES2 blend on the phone is unmeasured; builds in about 0.044 ms under jolt on the laptop with the draw side's own code run and the FFI stubbed, against the 0.30 ms target
Particles Blendingthe original's pool of 200 sparks (r, g, b from the LCG, a size of 1 to 30 over 20, falling 1.5 units a frame and fading 0.005 a frame) as flat circles of the sprite's size (32 times the size) tinted by their alpha, on DARKGRAY, with alpha or additive blending and 2 text lines; up to 200 circles, 1 button and 3 texts a frame, about 205 FFI calls59the pool, the draws in the order r g b size, the first-inactive activation, the fade, the fall, the order (spawn, then age, so a new particle ages that frame), the sprite's size, the tint (r, g, b, int(255 * alpha)), the texts and the clear are the original's, checked against the original's own loop over 400 frames; the mouse becomes a held finger, one particle a frame at the finger (the original spawns at the mouse wherever it is, so nothing is emitted here with no finger down); SPACE becomes a tap on a button that carries the original's label ("ALPHA BLENDING" in BLACK or "ADDITIVE BLENDING" in RAYWHITE) and flips on its release; the soft radial sprite is a flat circle, so the per-pixel falloff is gone and additive saturates sooner; the window is 800 units wide and as tall as the field, so a portrait field is taller than 450; the top text is centred and cut back to fit; the draw ends the blend mode in a finally; the GLES2 blend on the phone is unmeasured; builds in about 0.087 ms under jolt on the laptop with a full pool, a finger held and the draw side's own code run, against the 0.30 ms target
Billboard Renderingthe original's two billboards of side 2 at (0, 2, 0) and (1, 2, 1), one of them turning 0.4 degrees a frame, over a grid of 10 with the camera going round at 0.5 radians a second from 7.07 out and 4 up, projected in software (6 triangles each, up to 12 and 22 lines) and 1 line of text; about 0.13 ms a frame on the laptop with the draw side's code run, against the 0.30 ms target59the camera, the spin, the positions, the sizes and the grid are the original's, and a test compares every corner with a copy of the original's draw-billboard; the texture is redrawn as three flat squares (blue the whole billboard, red 0.7 of it, yellow 0.35) where the original's ring is circles, so the squares' corners are the difference and the squares visibly turn with the spin where the original's circles do not (discs of strips measured 0.62 ms, over the 0.30 ms target); the original's quad is the camera's mirror image where raylib's DrawBillboardPro is not, which this atlas hides; the original reads no input, so none is mapped; there is no finish, because the squares are coplanar and painted in order and the two billboards are on parallel planes, painted farther first by depth along the view axis where the original sorts by the distance of the centre from the camera (that is wrong for about 5.7 degrees of each lap, which its depth buffer hides); the caption sits below Back; the 3D view is clipped to the field
Directional Billboardthe original's figure on a billboard of side 1 at (0, 0.5, 0) over a grid of 10 with the camera going round at 0.5 radians a second on a circle of 2.83 and 1 up, projected in software (8 rectangles, 16 triangles, and 22 lines) and 1 line of text; about 0.16 ms a frame on the laptop with the draw side's code run58the row from the view angle (floor(atan2(z, x) / pi * 4 + 1/4), plus 8 when negative), the walk frame stepping when a timer passes 0.5 s, the orbit and the camera are the original's, and a test compares the row with a copy of the original's at about 2400 angles; the sheet's cell is redrawn as the two legs, body and head in value 0.5, 0.7 and 1 of hue 45 degrees a row, the legs, body and head pixel for pixel (all 32 poses checked against the original's rule) and the head as five rectangles that follow its circle row by row (the whole cell checked against the original's rule); the figure is the sheet mirrored left to right, as the original's quad draws it; the original reads no input, so none is mapped; its two text lines become one ("animation: N direction frame: N") below Back; there is no finish; the 3D view is clipped to the field
Textured Cubethe original's two cubes (2 by 4 by 2 at (-2, 2, 0), and 2 by 2 by 2 at (2, 1, 0)) over a grid of 10 with the camera going round at 0.01 radians a frame, 14 out and 8 up, projected in software (up to 30 triangles and 22 lines) and 1 line of text; about 0.15 ms a frame on the laptop with the draw side's code run (0.20 at worst), against the 0.30 ms target59the camera, its angle, the cubes, their sizes, the grid and the six faces' vertex lines with their texture coordinates are the original's, and a test compares every sub-quad's colour with the original's texture at that spot of that face, and its winding with the face's outward normal; there is no texture, so each face is drawn as the quads of its texels: the whole cube's face as the atlas's four quadrants (red, green, blue, yellow, cut at 0.5 as the 64 by 64 atlas has them, so nothing is lost to the cut) and the slice cube's face as one blue quad, since its slice u 0 to 0.5, v 0.5 to 1 lies in one quadrant; the slice cube's v runs the other way up, as the original's draw-cube-texture-rec has it; the original reads no input, so none is mapped; its text line sits below Back; there is no finish: the grid goes first, a convex cube needs only its back faces left out, and the cube on the camera's side of the plane x = 0 is painted last, which a test checks with rays; the 3D view is clipped to the field
Geometric Shapesthe original's shapes at its positions, sizes and colours: a red cube 2 by 5 by 2 with gold wires, maroon cube wires 3 by 6 by 2, a green sphere of radius 1, lime sphere wires of radius 2, a sky blue cylinder (top radius 1, bottom 2, 4 sides) with dark blue wires, brown cylinder wires (6 sides), a gold cone (8 sides) with pink wires and a violet capsule with purple wires, over a grid of 10, from the fixed camera at (0, 10, 10), projected in software (362 triangles and 1643 lines, 2005 items) and 1 line of text; 2.5 ms to build under jolt on the laptop and 0.36 ms a frame with the draw side's code run once built, above the 0.30 ms target but inside the edge where the phone held 59 to 60 fps (the bench stubs the draw loop, so the figure is rough)59the shapes, positions, sizes, colours, the camera and the grid are the original's, and the tessellation is too, with nothing cut: 16 by 16 for the sphere and its wires, 8 by 8 for the capsule; soft3d/cylinder, cylinder-wires, capsule and capsule-wires mirror DrawCylinder, DrawCylinderWires, DrawCapsule and DrawCapsuleWires vertex for vertex, and tests compare them with the C by hand for 4 sides and for the tilted capsule, and every triangle's winding with the outward normal; the 2.5 ms build is over the 0.30 ms target, and a cut of tessellation alone could not fit because drawing 2005 items is 0.36 ms by itself, so the list is built once per screen, the scene being static (a test checks that nothing but the screen changes it), and drawn every frame, with 2.5 ms on the laptop (about 80 ms on the phone at a rough 33x) spent once on the first frame and on each rotation, a single-frame hitch; the solid sphere is soft3d/sphere with {:shade :flat}, one colour as DrawSphere draws it, on that builder's latitude and longitude tessellation, and its wires are the C's DrawSphereWires as segments; there is no depth buffer, so finish sorts the triangles by mean depth and every wire draws over every face, which shows the far edges of the sky blue, gold and violet shapes, the maroon box's wires behind the red cube and the lime sphere's wires behind the shapes in front of it (the red cube's own gold wires leave out their hidden edges); the original's FPS counter is dropped and its "geometric shapes" text is the caption below Back; the original reads no input; the 3D view is clipped to the field
Basic Voxelthe original's 8 by 8 by 8 block of 512 unit voxels at the integer positions 0 to 7, BEIGE with BLACK wires, over a grid of 10, from a 45 degree camera that orbits the block's centre (radius 22, height 14, 0.006 radians an update) until you steer it, projected in software: only the exposed faces draw (384 on the full block), merged plane by plane into rectangles of one flat colour (the view from two sides is 4 triangles and 35 wire lines, from three sides 6 triangles and 51 lines; a block eaten full of holes is hundreds), a RED crosshair dot of radius 4 in the middle of the field, a voxel count as the caption below Back and a button for what a tap does59the world, the walk and look maths (forward (cos yaw, sin yaw), right (-sin yaw, cos yaw), SPEED 0.15, SENS 0.004, pitch held to +-1.4, the height never changing), the idle orbit with its hand-over, the crosshair as the ray and the nearest-voxel pick are the original's, with soft3d/screen->ray and ray-box standing in for its brute-force slab test; a relative thumb-stick started in the lower third of the field replaces WASD at one speed in every direction, a drag that starts in the upper two thirds replaces the mouse (0.004 radians a pixel scaled by 800 over the field's width), both at once, each by its own finger (net.b12n.raylib-ios.stick), a resting finger never adopted; a tap in the field is the left click and removes the voxel under the crosshair, wherever the tap lands, and it hands the camera over; a tap that moved, that ended a touch of two fingers or that began under Back does nothing; the original has no placing, so the button in the row under Back ("tap: remove" / "tap: place") is an addition: in place mode a tap puts a voxel on the face of the nearest voxel the ray entered, and does nothing on a miss, on an occupied cell or on the eye's cell; the frame counter and the original's two text lines are dropped (the caption is the voxel count); the 45 degree fovy is kept in a field as wide as 800x450 and widened in a narrower one; there is no depth buffer, so the wires draw over every fill (nothing is sorted, every fill being one opaque colour), which in a hollowed pit can show an edge of the far wall over the rim in front of it, and a face with a corner behind the near plane is dropped whole; wires that carry on in a straight line are joined into one line (same pixels, 35 instead of 280 from two sides); the cost follows the exposed faces and mostly the wires: under jolt on the laptop, with the draw side's code stubbed, a frame is 0.20 ms from two sides, 0.28 ms from three (the orbit's 45 degree corner) and 0.94 ms with a third of the block eaten in a scattered pattern (about three times the 0.30 ms target, and not timed on the phone), because each pit adds its walls (0.38, 0.56 and 1.3 before the merge and with a sort, which measured 32 to 57 fps on the phone); caching the exposed faces until the world changes, merging them, dropping the sort and joining wires were taken, no voxels were cut; a tap is ignored while another finger is down, so you stop walking to remove, where the original clicks while holding W; picking from inside the block takes the voxel the eye is in, as the original does
Doom-like Raycasterthe original's sixteen-row level with its six imps, cast by a DDA with one ray per column (180 columns, the original's 450) and drawn as flat vertical strips shaded by distance and side (a wall square on to the eye is one rect, 48 rects from the start and at most 221 in the poses measured), the imps as strips of rects tested against the wall behind them, the minimap (a panel, 96 wall cells, a dot for each living imp, the player and a heading line), the HUD bar with HEALTH, KILLS, IMPS, SHOTS and the fps and column count, the crosshair, YOU DIED and the muzzle flash, at the original's 900 by 560 proportions scaled to the field's width and placed at its top; a FIRE button in the field's lower right, the caption below Back and, while held, the stick's ring and knob; about 350 draw calls58the level, the DDA with its perpendicular distance, the wall height H / dist, the distance and side shade, the z-buffer with the sprite pass's 12 strips tested against it, rotate, move at 3.4 cells a second sliding along walls by testing each axis alone, shoot (the nearest living imp inside a 0.985 cone, walls ignored), the imps walking at 0.9 and biting within 0.9, the 0.06 second flash, the fps averaged over 0.4 seconds, dt held to 0.05 and the HUD and minimap are the original's, in its order of a frame; a relative thumb-stick begun on a fresh press in the lower half of the left half of the field replaces WASD (a unit vector, so one speed everywhere where the original's keys add, and the strafe is the original's along the camera plane so it is 0.66 of the forward speed), a drag in the right half replaces the mouse (0.0022 radians a pixel times 900 over the picture's width) and the arrow keys, both at once, each by its own finger (net.b12n.raylib-ios.stick), a resting finger never adopted; a finger that lands on FIRE fires once, as mouse-pressed? does, also while the other thumb walks, and never starts a stick or a turn; the cursor calls are dropped (hide-cursor, set-mouse-position, show-cursor: a phone has no pointer to hold at the window's middle), as is the quit key; 180 columns, not 450: the phone runs scene code about 33 times slower than the laptop and on the laptop a frame has to build in about 0.30 ms under jolt to hold 60 fps (that 0.30 is empirical, from the device pass, and not 16.7 ms divided by 33, which is 0.50), and 180 is the largest of the counts measured (100, 160, 180 and 200) whose worst pose fits: update, build and the draw side's loops with the FFI stubbed take 0.20 ms from the start, 0.29 ms at the worst of 2576 poses (every open cell centre by 16 headings) with and without six imps in view, and 0.28 ms at the worst of 1500 random poses with both thumbs held and the flash on (a column is 6.7 pixels wide on the phone, where the original's 450 are 2.7; at 100 columns the worst was 0.20 ms, at 160 0.27 and at 200 0.31); textured walls are flat: a wall is its style's colour from the original's own minimap, 150 70 55, 120 120 130, 90 110 150 and 70 180 110, times the original's shade as the vertex colour multiplies a texel, and the brick, stone, panel and circuit patterns are dropped; sprites are rects: each of the original's 12 strips draws the body (an ellipse), head (a circle) and eyes of the imp texel's column at its middle, depth-tested at its middle column as the original does, so a strip is the texel's colour in a flat bar and its edges are the strips' not the texture's; lossless cuts taken: no allocation per column, runs of columns that are the same rect in pixels merged into one, the strip extents worked out once, int for long on the hot path (a long of a float cost 100 ns under jolt, an int 25), and the original's bounds test and 64 step limit left out because the map's border is all wall and the player cannot cross a wall; the 900 by 560 picture is kept, so pixels are not square as in the original; the 'died' state freezes the imps but not the player, as in the original; on the phone it reads 58 idle and 58 to 59 while walking and turning
Texture Tilingone 64 by 64 texture drawn as a single quad over the whole screen, a translucent band with 2 lines of text, 2 buttons and their labels58the original's tile (a diagonal weave of BLUE and SKYBLUE over a dark ground with a GOLD dot in the middle), REPEAT wrap, and texcoords from the scroll to the scroll plus the density, which starts at 6.0, moves 0.08 a frame and is held to 1.0 to 24.0; the scroll grows 0.004 a frame; the first scene on a real GPU texture, uploaded once through rlgl when the scene opens (rlLoadTexture, 64 is a power of two, which GLES2 needs for REPEAT) and freed when you leave it; the screen is the picture, so the v range is the density times the screen's own height over width and the tiles stay square; UP and DOWN are two buttons under the text and a finger held on one repeats as the key does, at the original's 0.08 a frame; the count line is the original's tiles * tiles * (h / w) for the screen's own h / w; a reopen costs about one frame
Srcrec Dstrecone 384 by 64 texture drawn as a single rotated quad, two gray lines through the middle and a line of text58the original's sheet of six 64 by 64 frames (RED, ORANGE, GOLD, GREEN, SKYBLUE, VIOLET grounds with a RAYWHITE disc of radius 20.48), clamped because 384 is not a power of two; the fourth frame is the source rectangle (u from 192/384 to 256/384), the destination is a square at the middle of the screen turning about its own middle by 1 degree a frame, clockwise and unwrapped, like DrawTexturePro; the destination is 0.32 of the shorter side, not a whole 2x of the frame, and the vertical line starts below Back; uploaded once through rlgl when the scene opens and freed when you leave it; the first open after launch pauses about 0.2 s, and a reopen costs about one frame because the filled buffer is kept
Sprite Buttonone 160 by 144 texture drawn twice, as a button showing one third of it and as a preview of the whole sheet with a RED outline round the frame in use, plus 3 lines of text58the original's sheet of three 160 by 48 frames (normal, hover, pressed), clamped, and its slice by v0 = frame / 3 to v1 = (frame + 1) / 3; the mouse is touch: a finger down on the button shows the pressed frame and a release over it adds one to the click count, judged by where the finger was last down because the release's own position is not trusted; the hover frame shows while a finger that pressed the button has slid off it and is still down; the original's idle cycle through the frames is kept, since on a phone its live? is just a finger being down: with none down the three frames cycle, 60 frames each, and a finger landing stops it; the button is half the shorter side wide and the preview a quarter; the first open after launch pauses about 0.2 s, and a reopen costs about one frame because the filled buffer is kept
Npatch Drawingone 64 by 64 texture drawn as fifteen quads, a nine-patch and two three-patches, with a line of text, a hint, 3 panel labels and the source at its own size58the original's source (a bevelled border with gold corner studs round a quiet middle, BORDER 16), clamped, and its npatch! arithmetic written out as nine quads whose source rectangles carve the image 16 texels from each edge, so the corners keep their size while the edges and the middle stretch; a three-patch is the same routine with no top and bottom, or no left and right; the mouse is touch: a drag over the screen is the pointer, and the nine-patch grows to px - 380 by py - 150 (held to 40..360 by 40..250), the horizontal one to px - 60 and the vertical one to py - 150, all in the original's 800 by 450 units, which are scaled to the screen; until a finger lands the panels breathe on their own as the original's do, and a finger that lifts leaves them where it was; a reopen costs about one frame
Polygon Drawingone 256 by 256 hue wheel mapped onto a ten-sided polygon as thirty vertices in ten triangles, and a line of text58the original's wheel (the angle round the middle as a hue at full saturation and value), clamped because every uv lies in 0..1, and its fan of ten triangles from the middle at uv (0.5, 0.5) through the original's eleven texcoords, (uv - 0.5) * 256 as the points, turned by 1.0 degree a frame about the middle; the polygon is scaled so its farthest point sits at 45 percent of the shorter free side; the scene draws through triangles!, which winds each triangle itself, and there is nothing to touch; the first open after launch pauses about 0.8 s while the wheel is computed, and a reopen costs about one frame because the filled buffer is kept
Procedural Texturesfour 128 by 128 textures, a checkerboard, a gradient, noise and rings, each drawn as one quad with an outline and a name, and three lines of text58the original's four pixel functions, clamped and unfiltered as its textures are; the noise is a grey from the project LCG (high bits of one step a texel, in row order) in place of GetRandomValue, so a seed always replays the same picture; a tap is SPACE: a finger landing (not the frames it stays down, and not on Back) reseeds the noise and bumps its :version, which rewrites that one texture in place; the panels are a 2 by 2 grid on a portrait screen and a row of four on a landscape one, square, whichever is bigger; entering the scene fills all four textures once; the first open after launch pauses about 0.5 s, and a reopen about 0.14 s, since the noise is versioned and not kept (the other three textures reopen in about one frame)
Sprite Animationone 576 by 120 texture drawn twice, as the whole strip with the frame in use boxed RED and as that frame larger, plus 3 lines of text, 15 speed boxes and 2 buttons58the original's strip of six 96 by 120 frames, a stick walker painted by 15-circle limbs, a head and an eye through ImageDrawCircle (replayed through net.b12n.raylib-ios.texel, so it keeps raylib 6.0's circle one texel short on the right and bottom; the original's poses repeat, so six frames show three distinct poses), CLAMP wrap; the frame steps every 60 / speed updates, the speed runs 1 to 15 from 8, and the source rectangle is the only thing that changes after the one upload; LEFT and RIGHT are two buttons, SLOWER and FASTER, a press each; the first open after launch pauses about 1.0 s while the strip is drawn texel by texel, the longest in the gallery, and a reopen costs about one frame because the filled buffer is kept
Textured Curvea 64 by 128 road texture laid along a cubic Bezier as 3 to 48 quads (2 triangles each), 2 lines of text and 4 buttons58the original's road, asphalt with two white edge lines and four yellow dashes, REPEAT wrap and LINEAR filter (both sides are powers of two); each quad is square across the curve with the previous segment's normal on its near edge, and v accumulates by arc length, len / 256, so the dashes keep an even spacing; the middle two control points swing on their own at 0.015 t a frame, and nothing a finger does moves them; WIDTH - and WIDTH + repeat while held at 0.8 a frame between 6 and 80, SEG - and SEG + step the count once a press between 3 and 48; at the widest settings the ribbon can fold on a tight bend, and a folded triangle draws as a mirrored sliver where the original's RL_QUADS would be culled by rlgl; a reopen costs about one frame
Render Textureone 320 by 240 render target drawn into once (6 balls of radius 18, the text "off-screen" and a gray outline), then drawn back as 4 quads at 1, 0.6, 0.35 and 0.5 of its size, with 5 lines of text58the target, the balls' Lissajous (160 + 110 sin(t + 0.9 i), 120 + 70 cos(1.4 (t + 0.9 i))), their colours and the table of copies are the original's, and the last copy is tinted (255, 180, 180); the copies are drawn with :v0 1.0 :v1 0.0 because GL stores a target bottom-up; the original's 800 by 450 window is drawn as one block scaled to the screen and centred below Back, so a "100%" copy is the target at that block scale and not one texel to a pixel, and the text sizes scale with it; t is the seconds the scene has run, from :delta-seconds, in place of get-time; the target is 307 KB with no depth buffer, since the pass is 2D, zeroed on the CPU when the scene opens (0.18 ms under laptop jolt with the upload stubbed; on the phone the largest frame is 103 ms on a first open, which was the first scene opened after launch and so includes part of the launch), and freed when you leave; under laptop jolt a frame (update, draw loops, FFI stubbed) averages 0.014 ms
Framebuffer Renderingtwo render targets, each the size of its half of the field below Back (about 1206 by 1100 each in portrait), the observer's with a gold cube of side 2, pink wires, a grid of 10 and the other camera's green view frustum, the subject's with the same cube and a green 128 by 128 outline in the middle, then drawn back with a 128 by 128 inset of the subject's middle (the same texture sampled through a narrower rectangle), a green outline round it and a divider58the world, the two orbits (radius 5, height 2, 0.012 radians a frame, and radius 14, height 10, 0.004), fovy 45, the frustum built from the subject camera's own numbers at depth 3, the crop of 128, the inset 20 in from the subject view's corner and the texts are the original's; the views stack in portrait and sit side by side in landscape (as 3D Split Screen does) and each target is the size of its viewport in place of 400 by 450, so the crop and the inset, which stay 128 and 20 pixels, are small on a large half; the 3D is projected in software and drawn into each target as a 2D draw list, with no depth buffer, so the frustum is not depth tested: each of its 8 segments is painted under the cube when its midpoint is farther from the observer than the cube's centre and over it otherwise, an approximation where a long edge crossing the cube is wholly hidden or wholly drawn; the text sizes are the original's times the half's scale, cut back to fit; the original's FPS counter is dropped; the two targets are zeroed on the CPU when the scene opens (about 5.3 MB each in portrait, 2.1 ms for both under laptop jolt with the upload stubbed; on the phone the largest frame is 33 ms on a first open) and freed when you leave; under laptop jolt a frame (update, the draw-list builds and the draw loops, FFI stubbed) averages 0.15 ms
Mouse Paintingone render target the size of the field below the controls (about 1206 by 1924 in portrait), kept between frames and drawn back as one quad, a palette of 23 swatches, 4 buttons and a brush preview under the finger58the 23-colour palette, the brush (20, in steps of 5 between 2 and 50) and the paint, the erase to the first swatch and the clear to it are the original's; a finger is the mouse: it paints while it stays down, but only if it landed in the field, so a finger that slides in from the panel, or was already down when the scene opened, leaves no mark, and a finger that leaves the field ends its stroke and does not resume it on coming back (the original paints again on re-entry); the brush is the original's 20 pixels and is not scaled with the screen, so on a 1206 pixel wide screen it looks about two thirds the size it has in the original's 800 pixel window; the wheel is SIZE - and SIZE +, the right button is ERASER (a toggle, and picking a swatch turns it off) and C is CLEAR, each a press; the original draws one circle a frame and a finger moves far further than a mouse, so a stroke is circles spaced half a radius apart along the segment from the last point (at most 256 a segment), and the first point of a touch is one circle; the swatches wrap to two rows on a narrow screen so a thumb can hit them; the original's SAVE button and S key (a screenshot) and the hover highlight are dropped; turning the phone clears the picture: the canvas is the size of the field, so a rotation makes it again and it starts empty in the first swatch's colour (the original's window never turns); only the frame's marks are drawn into the canvas, none on a frame where nothing is touched; the canvas is about 9.3 MB with no depth buffer, since the passes are 2D, zeroed on the CPU when the scene opens (1.1 to 2.1 ms under laptop jolt with the upload stubbed; on the phone the largest frame is 29 ms on a first open) and freed when you leave; under laptop jolt a frame (update, the draw loops, FFI stubbed) averages 0.016 ms idle, 0.024 ms mid-stroke and 0.037 ms replaying 10 strokes
Magnifying Glassthe original's scene (a Perlin backdrop, seven squares with a circle each, 2 lines of one text) laid over the field below Back, and a 220 pixel lens that follows a finger, a render target of the same scene at 3x drawn back as a 64 wedge disc under a black ring58the constants (lens 220, zoom 3.0, 64 segments), the squares and circles and their colours, the five markers that show only in the lens (a LIME circle of 11 over a DARKGREEN one of 6), the lens's centring (ox = half - x * zoom), the flipped v of a bottom-up target, the ring 4 thick and the two texts are the original's; the backdrop is raylib's own GenImagePerlinNoise, 800 by 450 with offsets 0 and scale 6.0, made in C and uploaded straight from the buffer it fills (the pure net.b12n.raylib-ios.perlin model takes about 17 microseconds a texel under laptop jolt, 6 seconds for the image, and is the tested reference; the C takes about 20 ms on the laptop and 11.7 ms on the phone for 800 by 450), stretched over the field and drawn smooth where the original's is not; a finger is the pointer: the lens follows a finger that landed in the field, stays where it was left, and walks the original's slow path until the first touch; the lens centre is held a half lens inside the field, so a marker at the field's edge is reached with the lens at the edge; the scene is the original's 800 by 450 laid over the field as fractions (positions) and by the smaller side ratio (lengths), the lens stays 220 target pixels, so on the phone it is a small disc a finger covers; the lens target is 220 by 220 and the pixels of the image stay on the GPU while you are in the scene, a reopen makes the image again, and on the phone the largest frame is 30 ms on a first open; under laptop jolt a frame (update, the draw plans and loops, FFI stubbed) averages 0.16 ms
Top Down Lightsa ground of 64 pixel checks, 20 boxes (two placed by hand, 18 from the LCG), up to 16 lights, each with a mask the size of the field and one merged mask drawn over the ground in black, a circle on every light, 2 lines of help and a button58the constants (16 lights, 20 boxes, tile 64), the boxes, the shadow volumes (one quad per edge the light is outside of, plus the footprint, pushed out twice the radius), light 1 (radius 300, the others 200), its idle walk and the blend sequence are the original's: each dirty light is a mask cleared to WHITE, then a gradient drawn with GL_SRC_ALPHA, GL_SRC_ALPHA, GL_MIN, then its shadows with GL_SRC_ALPHA, GL_SRC_ALPHA, GL_MAX, and the master is cleared to BLACK and every mask merged with GL_MIN; the touch: a drag that travels past the tap slop moves light 1 and leaves it there, a quick tap adds a light (the right button), and a button toggles the shadow volumes (F1, which draws light 1's volumes in DARKPURPLE, the boxes it reaches in PURPLE and every box outlined in DARKBLUE); positions are the original's fractions of the field and lengths (boxes, radii, tile, markers) its scale; the help texts are raised to 24 pixels so a phone can read them; the original's FPS counter is dropped; this seed's box darkens the opening light: the idle walk starts at the field's centre, and with the seeded box placement a box sits there, so the light goes dark for its first frames until it walks out (the original's boxes are unseeded, so this is this port's placement, not its behaviour); a turn starts the scene again with one light, and the dropped lights' masks shrink to a pixel; each mask is the size of the field (about 1206 by 2214 in portrait, 10.7 MB of colour and no depth buffer, since the passes are 2D), so 16 lights hold 17 targets, about 182 MB (arithmetic, 17 times 10.7 MB, not a measurement), and each is zeroed on the CPU when made (about 1 ms under laptop jolt with the upload stubbed); on the phone it reads 58 with one light and 58 with 16, idle and while dragging a light, and the first open pauses 75 ms (119 ms when its targets still had depth buffers), with the shadows correct without depth; an earlier build that gave every target a depth buffer (an estimate of 272 to 363 MB) ran 16 lights without the app being ended; a frame redraws the masks of the lights that moved and merges all of them (laptop jolt, FFI stubbed): 0.08 ms with one light walking, 0.11 ms with 16 and one walking, 0.14 ms on the frame a tap adds a 16th
Raw Dataa checkerboard drawn 256 by 256 from one 64 by 64 repeating texture, and a 128 by 128 live panel, each drawn as one quad with an outline and a name, and 3 lines of text58the original's checkerboard (ORANGE and GOLD in squares of 32 texels, uploaded once as its one 64 by 64 period with :repeat and texcoords 0 to 4, which samples the same colour at every texel of the original's 256 by 256 panel) and its live panel (red, green and blue each an absolute sine or cosine of x, y and time, t = frame * 0.03), both unfiltered, the checkerboard repeating and the live panel clamped; the live panel is refreshed a band at a time: the original rewrites all 16384 texels every frame, which would stall the phone, so each frame refills a band of 3 rows (rlUpdateTexture with an offset and a size) walking down the panel, every row is refreshed once every 43 frames (the last band is 2 rows), about 1.4 times a second at 60 frames a second, and the animation drifts at that rate rather than the original's; the rows of one band share one t, so a moving seam shows between fresher rows above and older rows below, and the live panel's name says "a band at a time" in place of "every frame"; the fps readout is left out; entering the scene fills the 4096-texel checkerboard and the live panel once, a one-off cost; the panels stack on a portrait screen and sit side by side on a landscape one, square, with each name above its panel; the first open after launch pauses about 0.17 s, and a reopen about 0.15 s, since the live panel is banded and not kept (the checkerboard reopens in about one frame)
Screen Bufferone 100 by 56 texture of the classic DOS fire, scaled up to the width the free area allows and nothing else on screen58the original's 256-colour flame palette (hue 250 + 150 t^2, saturation and value both t) and its ember grid, with the roots growing 0 to 2 a step from column 2, the bottom row seeded from them, the top row blanked and every lit cell rising a row with a random drift and decay, clamped and unfiltered; the randomness is the project LCG, seeded in the state, in place of GetRandomValue, so a seed replays the same fire; the grid is a quarter of the original's: 100 by 56 against its 200 by 112 (raylib's C example uses 400 by 225), so the same rules give a relatively taller flame and chunkier cells; the roots start hot: from column 2 each is a random 192 to 255 (columns 0 and 1 stay dark, as in the original) instead of 0, so the first sweep already has a flame; the fire steps and uploads a band at a time: the original simulates the whole grid and rewrites every texel every frame, which would stall the phone, so each frame steps a band of 8 rows and uploads them with the row above (where their cells land), a sweep of 7 frames is exactly one of the original's steps, and so the fire follows the original's rules at about 8.6 steps a second instead of 60 (laptop jolt: a frame averages 0.19 ms, 0.29 ms at the 99th percentile); rows below the band are older than rows above it by up to 7 frames, so a moving seam shows between fresher and older rows; entering the scene fills all 5600 texels once, a one-off cost; the original has no text and neither does this; a reopen costs about one frame

Games

sceneper framefpsnotes
Flappy Bird~30 shapes59identical to the Android experiment apart from names and whitespace
Breakoutup to 60 bricks, a paddle, a ball58the paddle follows the finger, and a tap restarts after game over or a win; frame-locked like the original, and a rotation starts a new game
Snakeup to 576 cells, a board and a score line58a swipe steers and a tap restarts, in place of the arrow keys and SPACE; the 32 by 18 grid turns to 18 by 32 on a tall phone, and a rotation starts a new game
204816 tiles on a board, a score line58a swipe slides and a tap restarts once stuck or won, in place of the arrow keys and SPACE; reaching 2048 wins and stops play, which the original does not, and a rotation only re-lays out the board
Minesweeperup to 192 cells, a status line58a tap reveals and a long press flags, in place of left and right click; a tap restarts after a mine or a win, in place of SPACE; the 16 by 12 grid turns to 12 by 16 on a tall phone with the same 30 mines, a rotation starts a new game, and as in the original a tap on a flagged cell opens it and the first tap can hit a mine
Pong2 paddles, a ball, a dashed line, 2 scores58your paddle follows the finger and a tap restarts after a win, in place of W, S and ENTER; the court turns 90 degrees on a tall phone, with you at the bottom and the CPU at the top, and a rotation starts a new game; the CPU, the english off the paddle and first to 7 are the original's, and the ball does not speed up
Space Invadersup to 32 aliens, a ship, bullets, a score line58the ship follows the finger and fires while one is down, and a tap restarts after a loss or a win, in place of the arrow keys and SPACE; the 8 by 4 formation, its 1.2 px march, 18 px drops, the 15-frame fire cooldown and the loss rule (the lowest alien reaches the ship's row) are the original's, scaled to the safe region per axis, and a rotation starts a new game
Tetrisa 10 by 20 well, up to 4 falling cells, a next-piece preview, 3 counters59a horizontal drag moves the piece a column per cell of travel, a tap rotates, a swipe down hard drops and a tap restarts after game over, in place of the arrow keys, SPACE and ENTER; the release swipe of a drag is ignored so the piece does not move twice, and the soft drop is dropped; the seven pieces, the score table, the level speed (a frame count) and the uniform piece choice (from the project's LCG) are the original's, and a rotation starts a new game
Asteroidsoutlined rocks, a ship, bullets, 4 buttons, 2 counters58four buttons along the bottom (rotate left, rotate right, thrust, fire), held together by two thumbs, and a tap on the field, not on a button, restarts after game over, in place of the arrow keys, SPACE and ENTER; the physics (ROT, THRUST, FRICTION, wrap), the three asteroid sizes and splits, the 55-frame bullet life, the waves, 3 lives and the invulnerability blink are the original's; a press fires at once as the original's does, and a held fire button keeps firing every 10 frames, where the original fires once per press; the speeds and radii are scaled by one factor so thrust still goes where the ship points, the field wraps above the buttons and below Back, and a rotation starts a new game
Vampire Survivorsup to ~50 enemies and gems (through the first 12 s) and 18 bullets, a hero, two bars, 3 counters, a thumb-stick ring while held58a relative thumb-stick moves the hero, in place of WASD and the arrows: the press point is its centre, a finger within the tap slop of it does nothing, and beyond it the hero goes that way at one fixed speed, so a diagonal is no faster; a tap on the field restarts after game over, in place of ENTER; the spawn rate and wave growth, speeds, radii, two-hit enemies, the auto-fire at the nearest enemy and its level-based cooldown, gem pickup, levelling, contact damage with its grace period and 100 HP are the original's, frame-locked like it; the speeds and radii are scaled by one factor, enemies appear on the field's edge one radius inside it and never touching the hero where the original starts them 20 px outside, gems drawn centred (the original draws from the top-left corner), the field is below Back, and a rotation starts a new game
Pac-Man194 walls as two rectangles each, up to 197 dots, a 28-triangle Pac-Man, four ghosts of 9 shapes, a HUD row58a swipe sets the desired direction, in place of the arrow keys and WASD, and Pac-Man takes it at the next tile centre where that way is open, so a swipe into a wall is remembered and not dropped; a tap restarts after game over, outside Back, in place of ENTER; the maze, the four ghost personalities (Blinky, Pinky, Inky and Clyde), scatter for 7 s and chase for 20 s, 7 s of fright with the 200/400/800/1600 combo, scoring, three lives and the levels are the original's, in seconds from :delta-seconds like it (it calls get-frame-time); the level clear is fixed, because the original re-arms its two second timer every frame and never starts the next level, and the board freezes while LEVEL CLEARED shows; the ghosts' feet are spaced inside the body, where the original's third foot sticks out past it; the mouth is a 28-triangle fan, since sector! is not bound, the frightened ghosts' random turns come from the project's LCG, the maze is drawn with square tiles in the safe region below Back, and a rotation re-lays it out and keeps the game

What identical means

Three scenes and the three namespaces carrying the scene contract were copied from the Android experiment, and the only things that differ from the originals are the namespace names and whitespace. Put the upstream names back, collapse the whitespace, and the sha256 matches; a test and tools/extract-from-notebooks both check it.

They were written for Android. They run here unchanged apart from namespace names and whitespace, because the contract they were written against never mentions a platform: a scene is :init, :update, :draw and :dispose over immutable state, and every raylib call lives in each scene's draw namespace instead.

That is the whole argument for the split, and it is the reason most of the ports were transcription rather than rewrites. Not all of them: several needed resizing for a phone, and multitouch does something its original could not. See porting an example for what a port actually involves.

Adding one

A new sub-project and one line in demos.edn, then bb gen. The steps are in CONTRIBUTING.md: a pure .cljc under src/net/b12n/raylib_ios/scenes/, its draw-scene! method in scenes/<id>/draw.clj and a test beside them.