
Block Force Trauma

Design Digest
Design Digest
As a UX designer and strategist for Block Force Trauma, I helped evolve the core mechanics of our previous game, Rumble Tumble, into a new interactive format. I collaborated with my physical computing teammates to define the game’s logic, refine its player experience, and lead playtesting efforts. I also assisted in the coding and development of the digital assets, helped document rules, troubleshoot gameplay interactions, and ensure the game was intuitive, fun, and inclusive.
Introducing Block Force Trauma
Click to view the vision video
Discover
Discover
Through initial research and playtests, we discovered how much players loved the suspense and tactile thrill of physical building and random destruction. We also noticed opportunities to add more dynamic interactions and feedback, particularly around punishment and reward systems. Our challenge was to increase the replayability and interactivity of the game, ideally using responsive technology, without compromising its simplicity or accessibility.
Define
Define
Our goal: create a game where players could physically and digitally interact with their environment in a way that felt meaningful and strategic. The gamen needed to preserve the fun of physical block stacking while introducing a new mechanic to raise the stakes—reactive light blocks and randomized tower sabotage through “rumble cards.” We also prioritized modularity and accessibility, ensuring the rules were easy to follow while offering layers of depth.
Design
Design
We designed the board to include rumble response logic tied to each player’s button. If a rumble card was drawn, a microcontroller triggered lights in tower blocks—some of which would “shut off,” signaling players to remove them. I helped map out these rules, created early card prototypes, and coordinated gameplay UX documentation. We designed around fairness, randomization, and strategy, prototyping new card mechanics and rebalancing to ensure engaging dynamics. As lead coder, I helped ensure all digital assets functioned and were easy for end users to interact with.
Click to view the vision video
Laser Cutting: We mapped the dimensions of each type of block and transferred the file into illustrator for laser cutting. Our initial laser cutting ran into some errors and we got crunched for time. Today, we have the simplest blocks we’d like and may be increasing complexity as we go.
Test blocks with clear plastic:
Opaque laser cut plastic test blocks:
Wiring:
Assembly: We found various problems with the glue and adhesives we were using being messy (a problem warned to us). We found a way around this using a spray adhesive that works and cures very well. We also experimented with graphite powder in our poxy mix to make the adhesive itself conductive. We found that we needed to pay special attention to the RGB strips that we were using and which way they went-we knew they are directional but routinely put it in the wrong direction and had to restart.
A card was created for each block shape:
Special action cards were also included:
KANO Code:
//-----------------------------------------value set up
// random card
int card;
//rules page
int rules;
//-----------------------------------------image set up
// Intro
PImage Intro;
// Rules
//PImage GameSetUp; PImage BlockCards; PImage GameCards; PImage Winning;
PImage Rules;
// Cards
PImage BlockOne; PImage BlockTwo; PImage BlockThree; PImage BlockFour; PImage BlockFive;
PImage Give; PImage Reverse; PImage Rumble; PImage Skip;
void setup() {
size(1200, 800);
background (35, 31, 32);
rules = 0;
//-----------------------------------------load all images
// Intro
Intro = loadImage("Intro.jpg");
// Rules
Rules = loadImage("Rules.jpg");
// Cards
BlockOne = loadImage("BlockOne.jpg"); BlockTwo = loadImage("BlockTwo.jpg"); BlockThree = loadImage("BlockThree.jpg"); BlockFour = loadImage("BlockFour.jpg"); BlockFive = loadImage("BlockFive.jpg");
Give = loadImage("Give.jpg"); Reverse = loadImage("Reverse.jpg"); Rumble = loadImage("Rumble.jpg"); Skip = loadImage("Skip.jpg");
image(Intro, 0, 0);
}
void draw() {
}
void mouseClicked() {
// To New Game
//needs to route to Intro (top right hand corner)
if (mouseX >= 600 && mouseY <= 400) {
rules = 0;
image(Intro, 0, 0);
}
// To New Card
// needs to route to random card (bottom right hand corner)
if (mouseX >= 600 && mouseY >= 400) {
rules = 0;
// picking a card
card = int(random(9));
println(card);
switch (card) {
case 1:
image (BlockOne, 0, 0);
break;
case 2:
image (BlockTwo, 0, 0);
break;
case 3:
image (BlockThree, 0, 0);
break;
case 4:
image (BlockFour, 0, 0);
break;
case 5:
image (BlockFive, 0, 0);
break;
case 6:
image (Give, 0, 0);
break;
case 7:
image (Reverse, 0, 0);
break;
case 8:
image (Rumble, 0, 0);
break;
case 9:
image (Skip, 0, 0);
break;
}
}
// To Rules
// needs to route to rules (top left hand corner)
if(mouseX <= 600 && mouseY <= 400) {
image(Rules, 0,0);}
}
Arduino Code:
#include <FastLED.h>
#include <elapsedMillis.h>
#define NUM_LEDS 60
CRGBArray <NUM_LEDS> leds[5];
int color[5][3] = {
{0, 100, 75}, //red
{219, 100, 75}, //green
{142, 100, 75}, //blue
{300, 100, 75}, //yellow
{0, 0, 75} //white
};
int hues[5] = {map(color[0][0],0,360,0,255),map(color[1][0],0,360,0,255),map(color[2][0],0,360,0,255),map(color[3][0],0,360,0,255),map(color[4][0],0,360,0,255)};
int sats[5] = {map(color[0][1],0,100,0,255),map(color[1][1],0,100,0,255),map(color[2][1],0,100,0,255),map(color[3][1],0,100,0,255),map(color[4][1],0,100,0,255)};
int vals[5] = {map(color[0][2],0,100,0,255),map(color[1][2],0,100,0,255),map(color[2][2],0,100,0,255),map(color[3][2],0,100,0,255),map(color[4][2],0,100,0,255)};
int player1 = A0;
int player2 = A3;
int player3 = A2;
int player4 = A1;
int rumble = A4;
int SlatchPin = 11; // Latch pin of 74HC595 is connected to Digital pin 8
int SclockPin = 12; // Clock pin of 74HC595 is connected to Digital pin 7
int SdataPin = 10; // Data pin of 74HC595 is connected to Digital pin 4
byte butts = 0;
int death;
int wait = 10000;
int amount;
int wait2 = 5000;
int min_ = 2;
int max_ = 20;
unsigned int interval = 5000;
static uint8_t hue;
void setup() {
Serial.begin(9600);
pinMode(player1, INPUT_PULLUP); //Player 1
pinMode(player2, INPUT_PULLUP); //Player 2
pinMode(player3, INPUT_PULLUP); //Player 3
pinMode(player4, INPUT_PULLUP); //Player 4
pinMode(rumble, INPUT_PULLUP); //RuMbLe
FastLED.addLeds<NEOPIXEL,9>(leds[0], NUM_LEDS); //Player 1
FastLED.addLeds<NEOPIXEL,8>(leds[1], NUM_LEDS); //Player 2
FastLED.addLeds<NEOPIXEL,7>(leds[2], NUM_LEDS); //Player 3
FastLED.addLeds<NEOPIXEL,6>(leds[3], NUM_LEDS); //Player 4
FastLED.addLeds<NEOPIXEL,5>(leds[4], 100); //Base
pinMode(SlatchPin, OUTPUT);
pinMode(SdataPin, OUTPUT);
pinMode(SclockPin, OUTPUT);
}
void Player(elapsedMillis timeElapsed, int z, int interval){
while(timeElapsed < interval){
for(int i = 0; i < NUM_LEDS/2; i++){
leds[z].fadeToBlackBy(40);
leds[z][i] = CHSV(hues[z],sats[z],vals[z]);
leds[z](NUM_LEDS/2,NUM_LEDS-1) = leds[z](NUM_LEDS/2 - 1 ,0);
FastLED.delay(33);
}
}
randomSeed(analogRead(0));
amount = random(min_,max_);
for(int i = 0; i < NUM_LEDS/2; i++){
leds[z][i] = CHSV(0, 0, 0);
FastLED.show();
}
for(int i = 0; i < amount; i++){
leds[z][i] = CHSV(hues[z],sats[z],vals[z]);
FastLED.delay(33);
}
delay(wait2);
}
void updateShiftRegister(){
digitalWrite(SlatchPin, LOW);
shiftOut(SdataPin, SclockPin, LSBFIRST, butts);
digitalWrite(SlatchPin, HIGH);
}
void loop() {
butts = 0; // Initially turns all the LEDs off, by giving the variable 'leds' the value 0
updateShiftRegister();
elapsedMillis timeElapsed;
if(digitalRead(player1) == LOW){
bitSet(butts, 0);
updateShiftRegister();
Player(timeElapsed, 0, interval);
}else if(digitalRead(player2) == LOW){
bitSet(butts, 3);
updateShiftRegister();
Player(timeElapsed, 1, interval);
}else if(digitalRead(player3) == LOW){
bitSet(butts, 2);
updateShiftRegister();
Player(timeElapsed, 2, interval);
}else if(digitalRead(player4) == LOW){
bitSet(butts, 1);
updateShiftRegister();
Player(timeElapsed, 3, interval);
}else if(digitalRead(rumble) == LOW){
bitSet(butts, 4);
updateShiftRegister();
Rumble(timeElapsed);
}
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hues[z],sats[z],vals[z]);
}
FastLED.show();
}
}
void Rumble(elapsedMillis timeElapsed){
while(timeElapsed < interval){
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hue++,255,100);
}
FastLED.show();
}
}
randomSeed(analogRead(5));
death = random(0,3);
switch(death){
case 0:
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hues[0],sats[0],vals[0]);
}
FastLED.show();
}
//delay(wait);
break;
case 1:
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hues[1],sats[1],vals[1]);
}
FastLED.show();
}
//delay(wait);
break;
case 2:
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hues[2],sats[2],vals[2]);
}
FastLED.show();
}
//delay(wait);
break;
case 3:
for(int i = 0; i < NUM_LEDS/2; i++){
for(int z = 0; z < 5; z++){
leds[z][i] = CHSV(hues[3],sats[3],vals[3]);
}
FastLED.show();
}
//delay(wait);
break;
}
switch(death){
case 0:
Player(timeElapsed,0, 10000);
break;
case 1:
Player(timeElapsed,1, 10000);
break;
case 2:
Player(timeElapsed,2, 10000);
break;
case 3:
Player(timeElapsed,3, 10000);
break;
}
}
Hardware Processing Code for Board Lights:
//#include <FastLED.h>
//#define NUM_LEDS 30
//CRGBArray<NUM_LEDS> leds;
const int switch_12 = 12;
const int switch_11 = 11;
const int switch_10 = 10;
const int ledRed = 3;
const int ledRed = 5;
const int ledRed = 6;
int switchState_12;
int switchState_11;
int switchState_10;
void setup() {
//FastLED.addLeds<NEOPIXEL,9>(leds, NUM_LEDS);
Serial.begin(9600);
pinMode(ledRed, OUTPUT);
pinMode(ledBlue, OUTPUT);
pinMode(ledGreen, OUTPUT);
pinMode(switch_12, INPUT); //player 1
pinMode(switch_11, INPUT); //player 2
pinMode(switch_10, INPUT); //rumble
}
void loop() {
switchState_12 = digitalRead(switch_12);
switchState_11 = digitalRead(switch_11);;
switchState_10 = digitalRead(switch_10);;
if (switchState_12 ==HIGH)
//red
{digitalWrite(ledRed, 255);
digitalWrite(ledRed, 0);
digitalWrite(ledRed, 0);
}
else{
(switchState_11 ==HIGH)
//orange
{digitalWrite(ledRed, 255);
digitalWrite(ledRed, 0);
digitalWrite(ledRed, 165);
else{
(switchState_10 ==HIGH)
//orange
{digitalWrite(ledRed, 255);
digitalWrite(ledRed, 0);
digitalWrite(ledRed, 165);
}
// static uint8_t hue;
// for(int i = 0; i < NUM_LEDS/2; i++) {
// // fade everything out
// leds.fadeToBlackBy(40);
//
// // let's set an led value
// leds[i] = CHSV(hue++,255,255);
//
// // now, let's first 20 leds to the top 20 leds,
// leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0);
// FastLED.delay(33);
// }
}
"Sudden Death" Code:
//decide the amount of death
#include <FastLED.h>
#include <elapsedMillis.h>
#define NUM_LEDS 40
CRGBArray <NUM_LEDS> leds;
void setup() {
pinMode(11, INPUT);
FastLED.addLeds<NEOPIXEL,9>(leds, NUM_LEDS);
}
void loop() {
static uint8_t hue;
elapsedMillis timeElapsed;
unsigned int interval = 3000;
if(digitalRead(11) == HIGH){
while(timeElapsed < interval){
for(int i = 0; i < NUM_LEDS/2; i++){
leds.fadeToBlackBy(40);
leds[i] = CHSV(255,255,255);
leds(NUM_LEDS/2,NUM_LEDS-1) = leds(NUM_LEDS/2 - 1 ,0);
FastLED.delay(33);
}
}
int amount;
int wait2 = 5000;
randomSeed(analogRead(0));
amount = random(1,4);
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CRGB(0, 0, 0);
FastLED.show();
}
for(int i = 0; i < amount; i++){
leds[i] = CRGB(255, 0, 0);
FastLED.delay(33);
}
delay(wait2);
}else{
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CHSV(0, 0, 0);
FastLED.show();
}
}
}
"Random LED" Code:
//decide who dies
#include <FastLED.h>
#include <elapsedMillis.h>
#define NUM_LEDS 40
CRGBArray <NUM_LEDS> leds;
void setup() {
Serial.begin(9600);
pinMode(11, INPUT);
FastLED.addLeds<NEOPIXEL,9>(leds, NUM_LEDS);
}
void loop() {
static uint8_t hue;
elapsedMillis timeElapsed;
unsigned int interval = 5000;
if(digitalRead(11) == HIGH){
while(timeElapsed < interval){
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CHSV(hue++, 255, 100);
FastLED.show();
}
}
int death;
int wait = 10000;
randomSeed(analogRead(0));
death = random(0,3);
Serial.print(death);
switch(death){
case 0:
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CRGB(255, 0, 0);
FastLED.show();
}
delay(wait);
break;
case 1:
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CRGB(0, 255, 0);
FastLED.show();
}
delay(wait);
break;
case 2:
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CRGB(0, 0, 255);
FastLED.show();
}
delay(wait);
break;
case 3:
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CRGB(252, 3, 211);
FastLED.show();
}
delay(wait);
break;
}
}else{
for(int i = 0; i < NUM_LEDS/2; i++){
leds[i] = CHSV(0, 0, 0);
FastLED.show();
}
}
}
Blocks illuminate as they are placed:
Kano screen for the digital cards:
Sudden death!:
The strength of the magnets might be too strong for children to handle safely (snapping fingers). Our current magnets have 8.5-24 lbs of pull/hold (https://www.kjmagnetics.com/proddetail.asp?prod=RC62).
Solution: We found that washers are capable of reducing a magnet’s pull strength but not a magnet’s hold strength which would make them safer to interact with (https://www.kjmagnetics.com/blog.asp?p=block-or-extend). We now have about 20lbs hold but a weaker pull.
Only being able to stack the blocks in one direction due to the nature of magnets.
Solution: We overcame the problem of only being able to stack the blocks in one direction by putting one magnet down the middle of every block that will hold the data and the rest of the outer magnets will be the charged magnets.
Revised polarization map charting:
Deliver
Deliver
Through iterative prototyping and ongoing playtesting, we refined the game into a cohesive, chaos-fueled experience that still felt fair, strategic, and fun. We delivered a fully playable prototype that blended physical interaction with digital logic.
Debrief
Debrief
Block Force Trauma was a compelling next chapter in our game development journey. We proved that analog and digital design can meaningfully coexist in accessible, fun ways. Personally, I grew in translating physical experiences into interactive systems, bridging game design and UX thinking. Our playtesters responded positively to the new tech integration and appreciated the clarity of the revised ruleset—showing us the value of iterative user testing, system feedback, and collaborative design in crafting joyful chaos.