The experiment below (updated for Codea version 1.5.1), for exploring pages of Unicode characters, was created around the same time that I was experimenting with emoji. (Update: now also allows for an emoji-friendly font to be chosen, when required.)
-- -- Unicode Viewer -- supportedOrientations(LANDSCAPE_ANY) function setup() PageLow, PageHigh, Plane = 0, 0, 0 page = (Plane * 0x100 + PageHigh * 0x10 + PageLow) * 0x100 img = image(WIDTH, HEIGHT) parameter.integer("PageLow", 0, 15, PageLow, changed) parameter.integer("PageHigh", 0, 15, PageHigh, changed) parameter.integer("Plane", 0, 2, Plane, changed) parameter.boolean("isEmojiFriendly", true, efChanged) fill(255) stroke(0, 255, 0) strokeWidth(2.5) spriteMode(CENTER) textMode(CENTER) rectMode(CENTER) isZoomed = false pickedCharUnicode = nil pickedChar = nil print("Tap to zoom in") print("Tap to zoom out.") print() print("A white box means no character is defined for that code.") end function changed() page = (Plane * 0x100 + PageHigh * 0x10 + PageLow) * 0x100 latency = ElapsedTime + 0.08 redraw = true isZoomed = false end function efChanged() if isEmojiFriendly then f = "AppleColorEmoji" else f = "ArialMT" end drawPage(img, page) end function drawPage(img, base) setContext(img) background(0) fill(255) font(f) fontSize(40) for j = 0, 15 do for i = 0, 15 do local u = base + i * 0x10 + j local char = unicode2UTF8(u) local x = WIDTH/16 * (i + 1/2) local y = HEIGHT/16 * (15 - j + 1/2) text(char, x, y) end end setContext() end function draw() background(0) if redraw and ElapsedTime > latency then drawPage(img, page) redraw = false end if isZoomed then line(0, HEIGHT/2, WIDTH, HEIGHT/2) line(WIDTH/2, 0, WIDTH/2, HEIGHT) font(f) fontSize(512) local w, h = textSize(pickedChar) local fm = fontMetrics() noFill() rect(WIDTH/2, HEIGHT/2, w, h) fill(255) text(pickedChar, WIDTH/2, HEIGHT/2) fontSize(64) fill(0) text(pickedChar, WIDTH/2 + 1, 149) fill(255) text(pickedChar, WIDTH/2, 150) font("Inconsolata") fontSize(64) local title = string.format("U+%4X", pickedCharUnicode).. " (= "..pickedCharUnicode..")" text(title, WIDTH/2, 50) else sprite(img, WIDTH/2, HEIGHT/2) end end function touched(touch) if touch.state == BEGAN then if isZoomed then isZoomed = false else isZoomed = true local i = math.floor(touch.x/WIDTH * 16) local j = 15 - math.floor(touch.y/HEIGHT * 16) pickedCharUnicode = page + i * 0x10 + j pickedChar = unicode2UTF8(pickedCharUnicode) end end end -- Unicode code point to UTF-8 format string of bytes -- -- Bit Last point Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 -- 7 U+007F 0xxxxxxx -- 11 U+07FF 110xxxxx 10xxxxxx -- 16 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx -- 21 U+1FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -- 26 U+3FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- 31 U+7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- -- However, largest integer in Codea's Lua is 0x7FFFFF (23 bit) -- With acknowledgement also to Andrew Stacey's UTF-8 library function unicode2UTF8(u) u = math.max(0, math.floor(u)) -- A positive integer local UTF8 if u < 0x80 then -- less than 8 bits UTF8 = string.char(u) elseif u < 0x800 then -- less than 12 bits local b2 = u % 0x40 + 0x80 local b1 = math.floor(u/0x40) + 0xC0 UTF8 = string.char(b1, b2) elseif u < 0x10000 then -- less than 16 bits local b3 = u % 0x40 + 0x80 local b2 = math.floor(u/0x40) % 0x40 + 0x80 local b1 = math.floor(u/0x1000) + 0xE0 UTF8 = string.char(b1, b2, b3) elseif u < 0x200000 then -- less than 22 bits local b4 = u % 0x40 + 0x80 local b3 = math.floor(u/0x40) % 0x40 + 0x80 local b2 = math.floor(u/0x1000) % 0x40 + 0x80 local b1 = math.floor(u/0x40000) + 0xF0 UTF8 = string.char(b1, b2, b3, b4) elseif u < 0x800000 then -- less than 24 bits local b5 = u % 0x40 + 0x80 local b4 = math.floor(u/0x40) % 0x40 + 0x80 local b3 = math.floor(u/0x1000) % 0x40 + 0x80 local b2 = math.floor(u/0x40000) % 0x40 + 0x80 local b1 = math.floor(u/0x1000000) + 0xF8 UTF8 = string.char(b1, b2, b3, b4, b5) else print("Error: Code point too large for Codea's Lua.") end return UTF8 end ``` Drawing a page of 256 characters with `text()` takes a little time. To reduce the effects of latency on the parameter sliders, and when zooming in or out, I use an image (`img`) to hold the current page of characters and introduce a 0.08 second delay between the last change of the parameter sliders and the redrawing of the page (by `drawPage()`).