[turn back...]
silent hill 1 font exploration
here’s a demo i created to explore silent hill 1’s font. it’s not perfect but i hope you enjoy!
/* constants */
const CHAR_WIDTH = 24;
const CHAR_HEIGHT = 24;
const GRID_SIZE = 10;
const CHARMAP = `!"#$%&'()*+,—./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\`]^_'abcdefghijklmnopqrstuvwxyz{|}‾©~\"`;
const MARGIN_X = 30;
const TEXT_MARGIN_Y = 5;
/* state */
let currentString = "";
let gridDrawn = false;
/* objects */
const characterImages = [];
let spriteMap;
let textbox;
function preload() {
spriteMap = loadImage(window.SH1_SPRITE_PATH || "0.png");
}
const write = (string, drawBoundingBox = true) => {
let cursorX = 0;
let cursorY = 0;
Array.from(string).forEach((character) => {
// wrap text
if (cursorX + CHAR_WIDTH > width) {
cursorX = 0;
cursorY += CHAR_HEIGHT;
}
// handle spaces or invalid characters
const index = CHARMAP.indexOf(character);
if (character === " " || index < 0) {
cursorX += CHAR_WIDTH;
return;
}
const sprite = characterImages[index];
image(sprite, cursorX, cursorY);
if (drawBoundingBox) {
push();
if (window.SH1_FONT_BBOX_COLOR) {
stroke(window.SH1_FONT_BBOX_COLOR);
} else {
stroke(0, 255, 0, 255);
}
rect(cursorX, cursorY, sprite.width, sprite.height);
pop();
}
cursorX += sprite.width;
});
};
const loadCharacters = () => {
spriteMap.loadPixels();
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
// get bounding box of character
let startX = j * CHAR_WIDTH;
let startY = i * CHAR_HEIGHT;
let [minX, minY] = [startX + CHAR_WIDTH, startY];
let [maxX, maxY] = [startX, startY];
for (let k = 0; k < CHAR_WIDTH * CHAR_HEIGHT; k++) {
const x = startX + (k % CHAR_WIDTH);
const y = startY + Math.floor(k / CHAR_WIDTH);
const index = x + y * spriteMap.width;
if (spriteMap.pixels[index * 4 + 3] !== 0) {
// nonzero alpha
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
minX = Math.min(minX, x);
}
}
characterImages.push(
spriteMap.get(minX, minY, maxX - minX + 1, maxY - minY + 1)
);
}
}
};
const drawGrid = () => {
// create grid
translate(CHAR_WIDTH * (GRID_SIZE - 1) + MARGIN_X, 0);
for (let k = 0; k < GRID_SIZE * GRID_SIZE; k++) {
translate(CHAR_WIDTH, 0);
if (k % GRID_SIZE === 0) {
translate(-CHAR_WIDTH * GRID_SIZE, CHAR_HEIGHT);
}
rect(0, 0, CHAR_WIDTH, CHAR_HEIGHT);
write(CHARMAP.substring(k, k + 1));
}
translate(-CHAR_WIDTH * (GRID_SIZE - 1), CHAR_HEIGHT + TEXT_MARGIN_Y);
// text
write("silent hill", false);
translate(0, CHAR_HEIGHT);
write("type anything", false);
};
function setup() {
loadCharacters();
canvas = createCanvas(512, 330);
background(0);
noFill();
stroke(255);
textbox = createInput();
textbox.attribute("placeholder", "type something here...");
if (window.SH1_FONT_CANVAS_PARENT) {
const element = window.SH1_FONT_CANVAS_PARENT;
canvas.parent(element);
textbox.parent(element);
}
}
function draw() {
currentString = textbox.value();
if (currentString) {
// write the text
gridDrawn = false;
background(0);
write(currentString, false);
} else if (!gridDrawn) {
// visualize the sprite map
gridDrawn = true;
background(0);
drawGrid();
}
}