Glyph wave

Uses natiny.font.get_glyph metrics and natiny.render.text_ex to position, rotate, and color each glyph independently around its own center. The outlined rectangles show the glyph bounds relative to the baseline.


    
main.lua
local natiny = require("natiny")

natiny.backend.init()

local win = natiny.window.create("Glyph wave", 900, 600)
natiny.window.set_scale_mode(win, natiny.window.SCALE_MODE_STRETCH)

local res  = natiny.resource.load("common/fonts/roboto.ttf")
local font = natiny.font.create(res, 72)
local small = natiny.font.create(res, 18)

local WORD = "natiny"
local BG = natiny.color.from_hex("#151E2C")
local OBJECT = natiny.color.from_hex("#A9B3C5")
local LIGHT_TEXT = natiny.color.from_hex("#F3F5FA")
local ascent = natiny.font.get_ascent(font)
local time = 0

local function rainbow(phase)
    return natiny.color.from_rgba(
        0.55 + 0.45 * math.sin(phase),
        0.55 + 0.45 * math.sin(phase + 2.09),
        0.55 + 0.45 * math.sin(phase + 4.19),
        1.0)
end

natiny.backend.loop(function(dt)
    natiny.window.bind(win, function()
        time = time + dt

        local width = 0
        for _, code in utf8.codes(WORD) do
            width = width + natiny.font.get_glyph(font, code)
        end

        natiny.render.clear(BG)

        local pen, index = (900 - width) / 2, 0
        local baseline = 320

        natiny.render.set_color(OBJECT)
        natiny.render.line(60, baseline, 840, baseline, 1)

        for _, code in utf8.codes(WORD) do
            local advance, gx, gy, gw, gh = natiny.font.get_glyph(font, code)
            local phase = time * 3 - index * 0.6
            local bob = math.sin(phase) * 34
            local tilt = math.sin(phase) * 14

            natiny.render.set_color(OBJECT)
            natiny.render.rectangle_outline(pen + gx, baseline + gy + bob, gw, gh, 3)

            natiny.render.set_color(rainbow(phase))
            natiny.render.text_ex(font, pen + gx + gw / 2, baseline + gy + gh / 2 + bob,
                      utf8.char(code),
                      gx + gw / 2, ascent + gy + gh / 2, tilt)

            pen = pen + advance
            index = index + 1
        end

        natiny.render.set_color(LIGHT_TEXT)
        natiny.render.text(small, 60, 500,
               "each box is what get_glyph returns: the picture, relative to the pen")
    end)
end)

natiny.backend.shutdown()