Cyber Kamen

64 bits ought to be enough for anybody's face expression.
(WS2812B, ESP32 and Web Bluetooth API)

Similar projects worth following
Yet another cyberpunk mask (64-bit) with WS2812B.

As your alternate face, it blinks and shows a (64-)bit of emotions.
It's fully controllable with phones and PCs thanks to the BLE-enabled uC (ESP32) and Web Bluetooth API.
From the hardware aspect, it's basically just a cheap full face mask with the duct-taped LED matrix and some 3D printed light diffusers. About 15 USD and 30 minutes are required to build.

From darkarts.jpgD

From darkarts.jpg
From Ghost in the shell S.A.C 2nd gig
From Ghost in the shell S.A.C 2nd gig

A cool on-the-face gadget is indispensable to cyberpunk stories. I always wonder If I could have one. The difficulty is in designing and printing something not just stylish but also wearable. 

One day I found a neat full face mask is available at relatively inexpensive price ( 
I thought it'll be a good starting point to build my own cyberpunk mask and immediately bought two

"Two are really enough." From Blade Runner (1982)
"Two are really enough." From Blade Runner (1982)


Flexible diffuser for 8x8 WS2812B matrix

Standard Tesselated Geometry - 748.43 kB - 01/06/2022 at 16:49


  • 1 × Flexible 8x8 WS2812B matrix
  • 1 × MH-ET LIVE MiniKit(ESP32)

  • ESP32 and the non-standard Web Bluetooth API

    likeablob01/29/2022 at 12:13 0 comments

    By wearing this, you can watch Harry Potter and 50% of the Sorcerer's Stone.

    Instead of improving the duct-tape-made prototype-1, I started replacing a Digispark with ESP32 board to create some kind of Web-based controller. It's just because I'm interested in what can be achieved with Bluetooth Low Energy and Web Bluetooth.

    Although I used the word "non-standard" in the title of this entry, connecting the ESP32 and (Chromium-based) browser was pretty straightforward, thanks to the well-made abstraction layers of the both "arduino-esp32/libraries/BLE" (which is based on the Bluedroid stack) and Web Bluetooth API.

    I have not much to talk about as I have never seen any unstableness nor had to go deep into the stack or chromium/src.git. 
    So here I'd like to  share references which are probably useful for someone interested in the similar setup. 

  • Printing a flexible light diffuser

    likeablob01/29/2022 at 09:46 0 comments


    After checking that the "Face shield" and 8x8 WS2812B matrix (10 x 10 cm) nicely fit together at the size, I found the substrate of the LED matrix is a little bit visible by its light.So I decided to have a light diffuser for hiding it. Since the matrix tightly fits to the curved surface of the face shield, the diffuser must be flexible too. 

    It's time to launch OpenSCAD.

    Note: I eventually printed this parts up-side-down because the holes of the cells need to be fit accurately to WS2812B cells.

    The diffuser consists of "cells" for each WS2812B pixel and "tabs" which connect between "cells". Because the "tabs" are thin and have been printed with PLA, the structure got some flexibility.

    Let's assemble the diffuser and LED matrix and put a piece of paper on it for checking.

    Looks fine. Then the last thing to be done is pasting them together with some glue.

    That's it!

    It's a prototyping, so I allowed myself to use a ton of tapes to fix this assembly onto the face shield. (Hey, no need to feel guilty, even Apple uses tapes and glue in their products.)

    Wrote a test sketch to a beloved Digispark clone, now it started blinking.

    $ mkdir firmware
    $ pio init -b digispark-tiny -d firmware
    $ code firmware
    $ code firmware/platformio.ini
    platform = atmelavr
    board = digispark-tiny
    framework = arduino
    lib_deps =
    #include <Chrono.h>
    Chrono blinkTask;
    #include <Adafruit_NeoPixel.h>
    #ifdef __AVR__
    #include <avr/power.h>
    #define PIN 1
    #define NUMPIXELS 64
    Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    uint16_t hue = 200;
    int16_t saturation = 200;
    int16_t brightness = 10;
    const uint8_t face1[] = {0b0,        0b11000011, 0b11000011, 0b11000011,
                             0b11000011, 0b11000011, 0b11000011, 0b0};
    const uint8_t blink_frame_0[] = {0b0,        0b0,        0b11000011, 0b11000011,
                                     0b11000011, 0b11000011, 0b0,        0b0};
    const uint8_t blink_frame_1[] = {0b0,        0b0, 0b0, 0b11000011,
                                     0b11000011, 0b0, 0b0, 0b0};
    const uint8_t blink_frame_2[] = {0b0, 0b0, 0b0, 0b11000011, 0b0, 0b0, 0b0, 0b0};
    const uint8_t face2[] = {0b10000001, 0b01000010, 0b00100100, 0b00011000,
                             0b00011000, 0b00100100, 0b01000010, 0b10000001};
    void drawFrame(const uint8_t frame[]) {
        for(int i = 0; i < NUMPIXELS; i++) {
            uint8_t rowInd = i / 8;
            uint8_t row = frame[rowInd];
            uint8_t colInd = i % 8;
            if(rowInd % 2) { // reversing for the Zig-zag alignment
                colInd = 7 - colInd;
            uint8_t bit = (row >> (7 - colInd)) & 0b1;
            pixels.setPixelColor(i, pixels.ColorHSV(hue, saturation, bit ? 20 : 0));
    void randomColors() {
        for(int i = 0; i < NUMPIXELS; i++) {
                i, pixels.ColorHSV(rand() % UINT16_MAX, 200 + rand() % 50, 100));
    void blink() {
        const uint8_t *frames[] = {blink_frame_0, blink_frame_1, blink_frame_2,
                                   NULL,          blink_frame_1, blink_frame_0};
        for(auto &&i : frames) {
            if(i == NULL) {
    void setup() {
        for(int i = 0; i < NUMPIXELS; i++) {
            uint16_t hue_ = rand() % UINT16_MAX;
            for(size_t j = 0; j < brightness; j += 3) {
                pixels.setPixelColor(i, pixels.ColorHSV(hue_, saturation, j));
                if(i) {
                    pixels.setPixelColor(i - 1,
                                         pixels.ColorHSV(hue_, saturation, 0));
    void loop() {
        hue = hue + 1 % 655535;
        if(blinkTask.hasPassed(2000)) {

  • It starts blinking. (Prototyping with Digispark)

    likeablob01/06/2022 at 16:58 0 comments

View all 3 project logs

Enjoy this project?



Vlad V wrote 01/19/2022 at 18:52 point

is it seethrough? if so, im making one.

  Are you sure? yes | no

likeablob wrote 01/23/2022 at 15:41 point

@Vlad V 

Hi, Thanks for the comment.

Yes, but limited. About 20~30 deg around the center of the FoV is blocked by the LED matrix.
Probably It's not a good idea to wear this and hang around streets. : D

Anyway I'll upload some pictures of the prototype. (Currently I'm almost done on a new firmware based on ESP32 and a Web Bluetooth-based controller. )

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates