Skip to content

Commit 41b77c0

Browse files
Merge pull request #7 from bchainhub/update/min-01
Minified
2 parents 895c9a4 + 9cdf92e commit 41b77c0

File tree

7 files changed

+58
-98
lines changed

7 files changed

+58
-98
lines changed

Diff for: README.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ blo is a small and fast library to generate Blockchain identicons.
44

55
[![npm](https://img.shields.io/npm/v/@blockchainhub/blo?label=npm&color=cb3837&logo=npm)](https://www.npmjs.com/package/@blockchainhub/blo)
66
[![License: CORE](https://img.shields.io/badge/License-CORE-yellow?logo=googledocs)](LICENSE)
7-
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=Size&logo=tsnode)](https://bundlephobia.com/package/@blockchainhub/blo?label=Size)
7+
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=Size&logo=tsnode)](https://bundlephobia.com/package/@blockchainhub/blo@latest)
88
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue?logo=typescript)](https://www.typescriptlang.org/)
99
[![GitHub Sponsors](https://img.shields.io/github/sponsors/bchainhub?label=Sponsors&logo=githubsponsors&color=EA4AAA)](https://github.com/sponsors/bchainhub)
1010

1111
## Features
1212

13-
- 🐥 **Small**: **[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=&color=6ead0a)](https://bundlejs.com/?bundle&q=%40blockchainhub%2Fblo)** gzipped.
13+
- 🐥 **Small**: **[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=&color=6ead0a)](https://bundlephobia.com/package/@blockchainhub/blo@latest)** gzipped, distributed as minified ES modules.
1414
- 🔍 **Optimized**: Leverages SVG to generate compact and sharp images at any size.
1515
- 💆 **Simple**: Covering all blockchain networks, focusing on uniformity.
1616
- 🗂 **Typed**: Ships with [types included](#types).
@@ -21,12 +21,12 @@ blo is a small and fast library to generate Blockchain identicons.
2121

2222
Library | Renders/sec[^1] | Size[^2] | Types | Environment[^3] | Rendering
2323
--------|---------------:|------|--------|----------------|----------:
24-
**blo** | **500** | [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=&color=6ead0a)](https://bundlejs.com/?q=%40blockchainhub%2Fblo) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/all-6ead0a) | SVG
25-
blockies-react-svg | 620 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/blockies-react-svg?label=&color=ee4433)](https://bundlejs.com/?bundle&q=blockies-react-svg) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/react-ee4433) | SVG
26-
ethereum-blockies-base64 | 450 | [![Bundle Size](https://img.shields.io/bundlephobia/minzip/ethereum-blockies-base64?label=&color=ee4433)](https://bundlejs.com/?bundle&q=ethereum-blockies-base64) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/all-6ead0a) | PNG
27-
@download/blockies | 350 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/@download/blockies?label=&color=6ead0a)](https://bundlejs.com/?bundle&q=%6ead0a%2Fblockies) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/dom-ee4433) | Canvas
28-
blockies-ts | 360 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/blockies-ts?label=&color=6ead0a)](https://bundlejs.com/?bundle&q=blockies-ts) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/dom-ee4433) | Canvas
29-
react-blockies | 700 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/react-blockies?label=&color=ee4433)](https://bundlejs.com/?bundle&q=react-blockies) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/react-ee4433) | Canvas
24+
**@blockchainhub/blo** | **520** | [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@blockchainhub/blo?label=&color=6ead0a)](https://bundlephobia.com/package/@blockchainhub/blo@latest) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/all-6ead0a) | SVG
25+
blockies-react-svg | 580 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/blockies-react-svg?label=&color=ee4433)](https://bundlephobia.com/package/blockies-react-svg@latest) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/react-ee4433) | SVG
26+
ethereum-blockies-base64 | 450 | [![Bundle Size](https://img.shields.io/bundlephobia/minzip/ethereum-blockies-base64?label=&color=ee4433)](https://bundlephobia.com/package/ethereum-blockies-base64@latest) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/all-6ead0a) | PNG
27+
@download/blockies | 370 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/@download/blockies?label=&color=6ead0a)](https://bundlephobia.com/package/@download/blockies@latest) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/dom-ee4433) | Canvas
28+
blockies-ts | 370 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/blockies-ts?label=&color=6ead0a)](https://bundlephobia.com/package/blockies-ts@latest) | ![Types support](https://img.shields.io/badge/yes-6ead0a) | ![Environment support](https://img.shields.io/badge/dom-ee4433) | Canvas
29+
react-blockies | 700 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/react-blockies?label=&color=ee4433)](https://bundlephobia.com/package/react-blockies@latest) | ![Types support](https://img.shields.io/badge/no-ee4433) | ![Environment support](https://img.shields.io/badge/react-ee4433) | Canvas
3030

3131
[^1]: The number of renders per second. It was measured on Chromium Engine 131, MacOS with an Apple M2 Max. [See ./benchmark](https://github.com/blockchainhub/blo/tree/main/benchmark) for the methodology.
3232
[^2]: Minizipped bundle size. Good to be < 1 KiB.

Diff for: package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@blockchainhub/blo",
3-
"version": "1.2.4",
3+
"version": "1.2.5",
44
"license": "CORE",
55
"author": "Pierre Bertet <[email protected]>",
66
"contributors": [
@@ -34,7 +34,7 @@
3434
},
3535
"scripts": {
3636
"dev": "vite ./demos/react",
37-
"build": "tsc",
37+
"build": "tsc && esbuild dist/*.js --minify --outdir=dist --allow-overwrite",
3838
"ncu-all": "ncu -u && cd demos/bun && ncu -u && cd ../node && ncu -u && cd ../react && ncu -u && cd ../../benchmark && ncu -u"
3939
},
4040
"files": [
@@ -46,7 +46,7 @@
4646
"typescript": "^5.7.3",
4747
"vite": "^6.0.7",
4848
"vite-plugin-dts": "^4.5.0",
49-
"vitest": "^2.1.8"
49+
"esbuild": "^0.24.2"
5050
},
5151
"engines": {
5252
"node": ">=16"

Diff for: src/image.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Address, BloImage, BloImageData, Hsl, Palette, BloOptions } from "./types";
1+
import type { Address, BloImage, BloOptions, Hsl, Palette } from "./types";
22
import { seedRandom } from "./random";
33

44
// Pre-calculate constants
@@ -15,7 +15,7 @@ export function image(address: Address, options: BloOptions = {}): BloImage {
1515
return [data, palette];
1616
}
1717

18-
export function randomImageData(random: () => number): BloImageData {
18+
export function randomImageData(random: () => number): Uint8Array {
1919
// Use a single allocation
2020
const data = new Uint8Array(32);
2121

Diff for: src/index.ts

+9-17
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,24 @@
1-
import type { Address, BloImage, BloOptions } from "./types";
1+
import type { Address, BloOptions } from "./types";
22
import { image } from "./image";
33
import { svg } from "./svg";
44

55
export type {
66
Address,
77
BloImage,
8-
BloImageData,
98
BloOptions,
109
Hsl,
1110
Palette,
1211
PaletteIndex,
1312
} from "./types";
1413

15-
const DEFAULT_OPTIONS: BloOptions = {
16-
size: 64,
17-
seed: undefined
18-
};
14+
const defaultOpts: BloOptions = { size: 64 };
15+
const mergeOpts = (opts: BloOptions = {}) => ({ ...defaultOpts, ...opts });
1916

20-
export function blo(address: Address, options: BloOptions = {}): string {
21-
return "data:image/svg+xml;base64," + btoa(bloSvg(address, options));
22-
}
17+
export const blo = (a: Address, o: BloOptions = {}) =>
18+
"data:image/svg+xml;base64," + btoa(bloSvg(a, o));
2319

24-
export function bloSvg(address: Address, options: BloOptions = {}): string {
25-
const opts = { ...DEFAULT_OPTIONS, ...options };
26-
return svg(address, opts);
27-
}
20+
export const bloSvg = (a: Address, o: BloOptions = {}) =>
21+
svg(a, mergeOpts(o));
2822

29-
export function bloImage(address: Address, options: BloOptions = {}): BloImage {
30-
const opts = { ...DEFAULT_OPTIONS, ...options };
31-
return image(address, opts);
32-
}
23+
export const bloImage = (a: Address, o: BloOptions = {}) =>
24+
image(a, mergeOpts(o));

Diff for: src/random.ts

+12-20
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
1-
export function seedRandom(seed: string): () => number {
2-
const rseed = randSeed(seed);
3-
// based on Java’s String.hashCode(),
4-
// expanded to 4 32bit values
5-
return function random(): number {
6-
const t = rseed[0] ^ (rseed[0] << 11);
7-
rseed[0] = rseed[1];
8-
rseed[1] = rseed[2];
9-
rseed[2] = rseed[3];
10-
rseed[3] = rseed[3] ^ (rseed[3] >> 19) ^ t ^ (t >> 8);
11-
return (rseed[3] >>> 0) / (1 << 31 >>> 0);
12-
};
13-
}
14-
15-
function randSeed(seed: string): Uint32Array {
16-
// Xorshift: [x, y, z, w] 32 bit values
17-
const rseed = new Uint32Array([0, 0, 0, 0]);
18-
for (let i = 0; i < seed.length; i++) {
19-
rseed[i % 4] = (rseed[i % 4] << 5) - rseed[i % 4] + seed.charCodeAt(i);
1+
export const seedRandom = (s: string) => {
2+
const a = new Array(4).fill(0)
3+
for (let i = 0; i < s.length; i++) {
4+
a[i%4] = ((a[i%4] << 5) - a[i%4]) + s.charCodeAt(i)
5+
}
6+
return () => {
7+
const t = a[0] ^ (a[0] << 11)
8+
a[0] = a[1]
9+
a[1] = a[2]
10+
a[2] = a[3]
11+
a[3] = (a[3] ^ (a[3] >> 19) ^ t ^ (t >> 8))
12+
return (a[3]>>>0) / ((1 << 31)>>>0)
2013
}
21-
return rseed;
2214
}

Diff for: src/svg.ts

+21-42
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,26 @@ import type { Address, BloOptions } from "./types";
33
import { randomPalette } from "./image";
44
import { seedRandom } from "./random";
55

6-
// Pre-define constants
7-
const SVG_START = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8" shape-rendering="optimizeSpeed"`;
8-
const PATH_START = '<path fill="hsl(';
9-
const PATH_MID = ')" d="';
10-
const PATH_END = '"/>';
11-
12-
export function svg(address: Address, options: BloOptions = {}): string {
13-
const { size = 64, seed } = options;
14-
const random = seedRandom(seed || address.toLowerCase());
15-
const { background: b, primary: c, accent: s } = randomPalette(random);
16-
17-
// Pre-allocate arrays for better performance
18-
const paths = new Array(3);
19-
paths[0] = 'M0,0H8V8H0z'; // background
20-
paths[1] = ''; // color
21-
paths[2] = ''; // spot
22-
23-
// Unroll the loop for better performance
24-
let x, colorIndex;
25-
for (let i = 0; i < 32; i += 4) {
26-
x = i % 4;
27-
colorIndex = Math.floor(random() * 2.3);
28-
if (colorIndex > 0) paths[colorIndex] += `M${x},${i>>2}h1v1h-1zM${7-x},${i>>2}h1v1h-1z`;
29-
30-
x = (i + 1) % 4;
31-
colorIndex = Math.floor(random() * 2.3);
32-
if (colorIndex > 0) paths[colorIndex] += `M${x},${(i+1)>>2}h1v1h-1zM${7-x},${(i+1)>>2}h1v1h-1z`;
33-
34-
x = (i + 2) % 4;
35-
colorIndex = Math.floor(random() * 2.3);
36-
if (colorIndex > 0) paths[colorIndex] += `M${x},${(i+2)>>2}h1v1h-1zM${7-x},${(i+2)>>2}h1v1h-1z`;
37-
38-
x = (i + 3) % 4;
39-
colorIndex = Math.floor(random() * 2.3);
40-
if (colorIndex > 0) paths[colorIndex] += `M${x},${(i+3)>>2}h1v1h-1zM${7-x},${(i+3)>>2}h1v1h-1z`;
6+
export const svg = (a: Address, o: BloOptions): string => {
7+
const r = seedRandom(o.seed || a);
8+
const { background: b, primary: p, accent: c } = randomPalette(r);
9+
const s = o.size || 64;
10+
const scale = s / 8;
11+
const d = new Array(64);
12+
13+
for (let i = 0; i < 32; i++) {
14+
const x = i % 4, y = i / 4 | 0;
15+
const v = Math.floor(r() * 2.3);
16+
d[y * 8 + x] = v;
17+
d[y * 8 + (7 - x)] = v;
4118
}
4219

43-
// Use template literals for faster string concatenation
44-
return `${SVG_START} width="${size}" height="${size}">`
45-
+ `${PATH_START}${b[0]} ${b[1]}% ${b[2]}%${PATH_MID}${paths[0]}${PATH_END}`
46-
+ `${PATH_START}${c[0]} ${c[1]}% ${c[2]}%${PATH_MID}${paths[1]}${PATH_END}`
47-
+ `${PATH_START}${s[0]} ${s[1]}% ${s[2]}%${PATH_MID}${paths[2]}${PATH_END}`
48-
+ '</svg>';
49-
}
20+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${s}" height="${s}" viewBox="0 0 ${s} ${s}">
21+
<rect width="${s}" height="${s}" fill="hsl(${b[0]},${b[1]}%,${b[2]}%)"/>
22+
<g fill="hsl(${p[0]},${p[1]}%,${p[2]}%)">
23+
${d.map((v,i)=>v===1?`<rect width="${scale}" height="${scale}" x="${(i%8)*scale}" y="${(i/8|0)*scale}"/>`:'').join('')}
24+
</g>
25+
<g fill="hsl(${c[0]},${c[1]}%,${c[2]}%)">
26+
${d.map((v,i)=>v===2?`<rect width="${scale}" height="${scale}" x="${(i%8)*scale}" y="${(i/8|0)*scale}"/>`:'').join('')}
27+
</g></svg>`;
28+
};

Diff for: src/types.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// The data structure needed to render an icon.
2-
export type BloImage = [BloImageData, Palette];
2+
export type Address = string;
33

44
// 4x8 grid of the image left side, as 32 PaletteIndex items.
55
// The right side is omitted as it's a mirror of the left side.
6-
export type BloImageData = Uint8Array;
6+
export type BloImage = [Uint8Array, Palette];
77

88
// Colors used by a given icon.
99
export type Palette = {
@@ -26,9 +26,6 @@ export type PaletteIndex = 0 | 1 | 2;
2626
// [2]: 0-100 (lightness)
2727
export type Hsl = Uint16Array;
2828

29-
// An Ethereum address.
30-
export type Address = string;
31-
3229
// Add size constraints
3330
export type ValidSize = number;
3431
// Or more strictly:
@@ -46,6 +43,6 @@ export type HslValues = {
4643
};
4744

4845
export interface BloOptions {
49-
size?: ValidSize;
46+
size?: number;
5047
seed?: string;
5148
}

0 commit comments

Comments
 (0)