Final Project

At the moment, my project is a clear case of “it worked 5 minutes ago”, so this is just the first is a series of updates. However, it was working at that time quite well, but that time was less than 10 minutes, which only gave enough time for me to test it. User testing will be coming tomorrow. For its current state though, it works!

The remaining bug is most likely power based, and therefore connection based, so I won’t solve it until I solder the connections on the back.

I have also tested the flippers support the weight of the ball, and that the ball correctly controls the advancement of control of the flippers along the path.

 

For my own interaction testing, the best results were generated with full bod moments at a significant distance, this should be easily fine tuned when the game is in its final location.

Space Invaders V-2

This is a fairly straightforward upgrade on my last iteration of Space Invaders to use an analog thumb joystick instead of keys. I also made a few changes to the code, specifically changing the scoring system, allowing the ship to move in more directions, making it autoshoot, and allowing the ship to collide with the enemies (which kills the enemy and looses a life).

The new code is

import processing.serial.*;
Serial myPort;
Game game;
float divisions= 50;
void setup() {
  printArray(Serial.list());
  String portname=Serial.list()[0];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  size(500, 500);
  game= new Game();
  frameRate(30);
}
void draw() {
  game.draw();
  if (!game.update()) {
    int score= game.getScore();
    game=null;
    background(0);
    textSize(32);
    text("Game Over", width/7, height/2-50);
    text("Score: "+score, width*3/7, height/2+50);
    noLoop();
  }
}
void keyPressed() {
  game.keyPressed(key);
}
void serialEvent(Serial myPort) {
  game.serialEvent(myPort);
}
import java.util.Iterator;
class Game {
  ArrayList<Enemy> enemies;
  Ship ship;
  ArrayList<Projectile> projectiles;
  float timer;
  float enemyRate= 500;
  float levelTimer;
  float levelRate= 30000;
  int lives;
  int score;
  int scoreStep;
  Game() {
    enemies= new ArrayList<Enemy>();
    ship= new Ship(this);
    projectiles= new ArrayList<Projectile>();
    timer= millis();
    levelTimer= millis();
    lives= 10;
    scoreStep=1;
  }
  void keyPressed(int key) {
    ship.keyPressed(key);
  }
  boolean update() {
    Enemy enemy;
    Iterator<Enemy> enemyIterator= enemies.iterator();
    while (enemyIterator.hasNext()) {
      try {
        enemy=enemyIterator.next();
      }
      catch(Exception e) {
        break;
      }
      enemy.update();
      Projectile projectile;
      Iterator<Projectile> projectileIterator= projectiles.iterator();
      if (enemy.reachedEnd(height-ship.sHeight)) {
        enemies.remove(enemyIterator);
        enemies.remove(enemy);
        score-=100;
        lives--;
      }
      if (enemy.checkImpact(ship)) {
        enemies.remove(enemyIterator);
        enemies.remove(enemy);
        score-=100;
        lives--;
      }
      while (projectileIterator.hasNext()) {
        try {
          projectile=projectileIterator.next();
        }
        catch(Exception e) {
          break;
        }
        if (projectile.checkImpact(enemy)) {
          enemies.remove(enemyIterator);
          enemies.remove(enemy);
          projectiles.remove(projectileIterator);
          projectiles.remove(projectile);
          score+=scoreStep;
        }
      }
      if (levelTimer+levelRate<millis()) {
        enemyRate/=1.3;
        scoreStep*=2;
        levelTimer=millis();
      }
    }
    ship.update();
    for (Projectile projectile : projectiles) {
      projectile.update();
    }
    if (timer+enemyRate<millis()) {
      enemies.add(new Enemy(width/divisions, height/divisions, game));
      timer=millis();
    }
    return lives>0;
  }
  void draw() {
    background(0);
    for (Enemy enemy : enemies) {
      enemy.draw();
    }
    ship.draw();
    for (Projectile projectile : projectiles) {
      projectile.draw();
    }
    fill(255);
    textSize(10);
    text("lives: "+lives, 10, height-10);
    text("score: "+score, width-80, height-10);
  }
  void addProjectile(Projectile projectile) {
    projectiles.add(projectile);
  }
  int getScore() {
    return score;
  }
  void serialEvent(Serial myPort) {
    ship.serialEvent(myPort);
  }
}
class Projectile{
  float x;
  float y;
  float xVelocity;
  float yVelocity;
  Projectile(float x, float y){
    this.x=x;
    this.y=y;
  }
  void update(){
    x+=xVelocity;
    y+=yVelocity;
  }
  void draw(){
    stroke(255);
    point(x,y);
  }
  boolean checkImpact(Enemy enemy){
    return((x-enemy.getX())*(x-enemy.getX())+(y-enemy.getY())*(y-enemy.getY()))<enemy.getR()*enemy.getR();
  }
}
class Enemy {
  float x;
  float y;
  float r;
  float xVelocity;
  int direction;
  Game game;
  Enemy(float x, float y, Game game) {
    this.game=game;
    this.x=x;
    this.y=y;
    r=sqrt(width*height/(divisions*divisions));
    xVelocity=r/2;
    direction=1;
  }
  void update() {
    if (r<x&&x<width-r||direction==0) {
      if (direction==0) {
        if (x<width/2) {
          direction=1;
        } else {
          direction=-1;
        }
      }
      x+=xVelocity*direction;
    } else if (x<=r&&direction!=0) {
      y+=2*r;
      direction=0;
    } else if (x>=width-r&&direction!=0) {
      y+=2*r;
      direction=0;
    }
  }
  void draw() {
    fill(255);
    ellipse(x, y, r, r);
  }
  float getX() {
    return x;
  }
  float getY() {
    return y;
  }
  float getR() {
    return r;
  }
  boolean reachedEnd(float y) {
    return this.y>y;
  }
  boolean checkImpact(Ship ship) {
    return((x-ship.getX())*(x-ship.getX())+(y-ship.getY())*(y-ship.getY()))<ship.getR()*ship.getR();
  }
}
class Ship{
  float x;
  float step;
  float y;
  boolean shooting;
  float sWidth;
  float sHeight;
  float timer;
  float fireRate;
  float step0;
  Game game;
  Ship(Game game){
    this.game= game;
    x= width/2;
    step= width/(divisions*2);
    y= width*(divisions-2)/divisions;
    sWidth= width/divisions;
    sHeight= height/divisions;
    timer= millis();
    fireRate= 250;
    shooting=false;
    step0=step;
  }
  void keyPressed(int key){
    if(key=='x'&&x<width-sWidth){
      x+= step;
    }
    else if(key=='z'&&x>sWidth){
      x-= step;
    }
    else if(key==' '){
      shooting=!shooting;
    }
  }
  void serialEvent(Serial myPort){
    String s=myPort.readStringUntil('\n');
    s=trim(s);
    if (s!=null){
      int values[]=int(split(s,','));
      int X= 0, Y=0;
      if (values.length==3){
        X=(int)map(values[0],0,1023,-step, step);
        Y=(int)map(values[1],0,1023,-step, step);
        if(values[2]>0){
          step=step0/2;
          shooting=true;
        }
        else{
          shooting=false;
          step=step0*2;
        }
      }
      x+=X;
      y-=Y;
      if(x<0){
        x=sWidth;
      }
      if(x>width){
        x=width-sWidth;
      }
      if(y<0){
        y=sHeight;
      }
      if(y>height){
        y=height-sHeight;
      }
    }
  }
  void update(){
    if(timer+fireRate<millis()&&shooting){
      shoot();
      timer= millis();
    }
  }
  void draw(){
    triangle(x-sWidth/2,sHeight+y,x,y,x+sWidth/2,sHeight+y);
  }
  void shoot(){
    game.addProjectile(new ShipProjectile(x,y));
  }
  float getY(){
    return y;
  }
  float getX(){
    return x;
  }
  float getR(){
    return sWidth;
  }
}
class ShipProjectile extends Projectile{
  ShipProjectile(float x, float y){
    super(x,y);
    xVelocity=0;
    yVelocity=-height/divisions*3/4;
  }
}

The arduino code is super simple:

const int X= A0;
const int Y= A1;
const int Z= A2;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
 // if(Serial.available()>0){
    int x = analogRead(X);
    delay(1);
    int y = analogRead(Y);
    delay(1);
    int z = analogRead(Z);
    delay(1);
    Serial.print(x);
    Serial.print(',');
    Serial.print(y);
    Serial.print(',');
    Serial.println(z);
  //}
}

 

What does Computing mean to me

I don’t think computing on its own is a significant concept to me. It is such a huge part of my life, between this class, my computer science minor, and my physics research, that i basically don’t see it anymore, just specific applications of it. I used so see it as something amazing and wonderful, that was a very pleasant break from my other classes, like languages. Now though, I see that its inherent logic is convoluted by people in any of the projects that are sufficiently complicated to be worth something, and so while simple coding and computing has some inherent beauty for me, most is so hacky and misleading that I see it mostly as a powerful tool. It is necessary for a great many things, but it is only a necessary feature, it does not increase the value of whatever it is involved in inherently.

Data Visualization

First, I should note that I could not find the correct library to access the data I was trying to use in Java, so I am using random data.

When presented with this prompt, my first thought was my capstone, which is largely data visualization. However, it uses software I could not recreate in less than a couple years, so I decided to process data similar to my capstone in a visually interesting way. In other words, I wanted to take a large set of floats mapped to 2d space, and group them into maxima. This is a very simple way to describe how stars are found in a digital image, which is roughly what I seek to recreate. I did this, but it was a bit boring, so I also set it up to make a more dynamic image that follows similar logic. These are the two modes in my program, mode 0 is more interesting but mode 1 is more intuitive.

float[][] visualization;
float[][] tempVisualization;
float max=0;
float min=255;
float range=255;
float growthfactor=255;
int mode= 0;
//loat[] data;
ImageHDU imageHdu;
void setup(){
  size(100,100);
  frameRate(15);
  //data = getImageHDUDataInFloatArray(imageHdu);
  visualization= new float[width][height];
  tempVisualization= new float[width][height];
  for(int x= 0; x< width; x++){
    for(int y= 0; y< height; y++){
     visualization[x][y]= random(25);
     //println(visualization[x][y]);
    }
  }
  
}

void draw(){
  min=255;
  max=0;
  for(int x= 1; x< width-1; x++){
    for(int y= 1; y< height-1; y++){
       if(mode==0){
         if(visualization[x][y]>visualization[x][y+1]&&
         visualization[x][y]>visualization[x][y-1]&&
         visualization[x][y]>visualization[x+1][y+1]&&
         visualization[x][y]>visualization[x-1][y+1]&&
         visualization[x][y]>visualization[x+1][y-1]&&
         visualization[x][y]>visualization[x-1][y-1]&&
         visualization[x][y]>visualization[x-1][y]&&
         visualization[x][y]>visualization[x+1][y]){
           tempVisualization[x][y]=visualization[x][y]+
           (visualization[x-1][y]+
           visualization[x-1][y-1]+
           visualization[x][y-1]+
           visualization[x+1][y-1]+
           visualization[x-1][y+1]+
           visualization[x][y+1]+
           visualization[x+1][y+1]+
           visualization[x+1][y])/2;
         }
         else{
           tempVisualization[x][y]=visualization[x][y]-
           (visualization[x-1][y]+
           visualization[x-1][y-1]+
           visualization[x][y-1]+
           visualization[x+1][y-1]+
           visualization[x-1][y+1]+
           visualization[x][y+1]+
           visualization[x+1][y+1]+
           visualization[x+1][y])/9;
         }
       }
     if(mode==1){
       for(int x2=-1; x2<2; x2++){
         for(int y2=-1; y2<2; y2++){
           if(x2!=0||y2!=0){
             process(x,y,x+x2,y+y2);
           }
         }
       }
     }
     max=max(max,tempVisualization[x][y]);
     min=min(min,tempVisualization[x][y]);
     stroke(tempVisualization[x][y]);
     point(x,y);
    }
  }
  range=max-min;
  range=min(range,254);
  if(mode==1){
    growthfactor=range*0.7;
  }
  if(mode==0){
    for(int x= 0; x< width; x++){
      for(int y= 0; y< height; y++){
       visualization[x][y]= visualization[x][y]*growthfactor/range;
      }
    }
  }
  println("looped");
  visualization=tempVisualization;
}
public void process(int largeX, int largeY, int smallX, int smallY){
  if(visualization[largeX][largeY]>visualization[smallX][smallY]){
     tempVisualization[largeX][largeY]+=visualization[smallX][smallY];
   }
   else{
     tempVisualization[largeX][largeY]=tempVisualization[largeX][largeY]*growthfactor/range;
   }
}

The file type I was attempting to use if fits, which I know the library for in python but not java.

The Digitization of Just About Everything

This is perhaps the most straightforward reading we have had, that finally outlines the basic differences between digital and physical products. It is not particularly exciting or revolutionary, but is important in that it clearly explains that it clearly identifies why the digital economy is consuming the physical one at every opportunity. Unlike physical goods, it costs next to nothing to continue to reproduce the product. I have seen this to a large extent myself in video games. When I was younger games came on physical disks, and consistently cost about 60 dollars. While the standard price has not changed, most games are now sold digitally, and at least on steam frequently go on sale for up to 90% off, since any sale at all is a profit.

Space Invaders

In describing this project, space invaders was mentioned a bunch of times, and I decided that it sounded reasonably fun to work on.

I set up my game as if there would be multiple enemies who would shoot back, though in my current implementation enemies win by reaching the end, and are all simple circles, ship the ship is a triangle.

The main code is very simple, creating a game and having the win logic.

Game game;
float divisions= 50;
void setup(){
  size(500,500);
  game= new Game();
  frameRate(30);
}
void draw(){
  game.draw();
  if(!game.update()){
    int score= game.getScore();
    game=null;
    background(0);
    textSize(32);
    text("Game Over",width/7,height/2-50);
    text("Score: "+score,width*3/7,height/2+50);
    noLoop();
  }
  
}
void keyPressed(){
  game.keyPressed(key);
}

The game is also simple, just facilitating the interaction of the enemies, ship, and projectiles, and containing each. Also tracks the larger game logic like score and lives.

import java.util.Iterator;
class Game{
  ArrayList<Enemy> enemies;
  Ship ship;
  ArrayList<Projectile> projectiles;
  float timer;
  float enemyRate= 500;
  float levelTimer;
  float levelRate= 30000;
  int lives;
  int score;
  Game(){
    enemies= new ArrayList<Enemy>();
    ship= new Ship(this);
    projectiles= new ArrayList<Projectile>();
    timer= millis();
    levelTimer= millis();
    lives= 10;
  }
  void keyPressed(int key){
    ship.keyPressed(key);
  }
  boolean update(){
    Enemy enemy;
    Iterator<Enemy> enemyIterator= enemies.iterator();
    while(enemyIterator.hasNext()){
      try{
          enemy=enemyIterator.next();
        }
        catch(Exception e){
          break;
        }
      enemy.update();
      Projectile projectile;
      Iterator<Projectile> projectileIterator= projectiles.iterator();
      if(enemy.reachedEnd(ship)){
        enemies.remove(enemyIterator);
        enemies.remove(enemy);
        score-=100;
        lives--;
      }
      while(projectileIterator.hasNext()){
        try{
          projectile=projectileIterator.next();
        }
        catch(Exception e){
          break;
        }
        if(projectile.checkImpact(enemy)){
          enemies.remove(enemyIterator);
          enemies.remove(enemy);
          projectiles.remove(projectileIterator);
          projectiles.remove(projectile);
          score++;
        }
      }
      if(levelTimer+levelRate<millis()){
        enemyRate/=1.3;
        score*=1.3;
        levelTimer=millis();
      }
    }
    ship.update();
    for(Projectile projectile: projectiles){
      projectile.update();
    }
    if(timer+enemyRate<millis()){
      enemies.add(new Enemy(width/divisions,height/divisions,game));
      timer=millis();
    }
    return lives>0;
  }
  void draw(){
    background(0);
    for(Enemy enemy: enemies){
      enemy.draw();
    }
    ship.draw();
    for(Projectile projectile: projectiles){
      projectile.draw();
    }
    fill(255);
    textSize(8);
    text("lives: "+lives,10,10);
    text("score: "+score,width-50,10);
  }
  void addProjectile(Projectile projectile){
    projectiles.add(projectile);
  }
  int getScore(){
    return score;
  }
}

The first component class is ship, moves according to controls (z,x,’ ‘), mostly just creates projectiles

class Ship{
  float x;
  float step;
  float y;
  boolean shooting;
  float sWidth;
  float sHeight;
  float timer;
  float fireRate;
  Game game;
  Ship(Game game){
    this.game= game;
    x= width/2;
    step= width/divisions;
    y= width*(divisions-2)/divisions;
    sWidth= width/divisions;
    sHeight= height/divisions;
    timer= millis();
    fireRate= 250;
    shooting=false;
  }
  void keyPressed(int key){
    if(key=='x'&&x<width-sWidth){
      x+= step;
    }
    else if(key=='z'&&x>sWidth){
      x-= step;
    }
    else if(key==' '){
      shooting=!shooting;
    }
  }
  void update(){
    if(timer+fireRate<millis()&&shooting){
      shoot();
      timer= millis();
    }
  }
  void draw(){
    triangle(x-sWidth/2,sHeight+y,x,y,x+sWidth/2,sHeight+y);
  }
  void shoot(){
    game.addProjectile(new ShipProjectile(x,y));
  }
  float getY(){
    return y;
  }
}

Since in theory projectiles can be made by enemies (though collision detection would need a re-write, mostly an if instance of or separate detection logic for each projectile type.), there is a parent and child version

class Projectile{
  float x;
  float y;
  float xVelocity;
  float yVelocity;
  Projectile(float x, float y){
    this.x=x;
    this.y=y;
  }
  void update(){
    x+=xVelocity;
    y+=yVelocity;
  }
  void draw(){
    stroke(255);
    point(x,y);
  }
  boolean checkImpact(Enemy enemy){
    return((x-enemy.getX())*(x-enemy.getX())+(y-enemy.getY())*(y-enemy.getY()))<enemy.getR()*enemy.getR();
  }
}
class ShipProjectile extends Projectile{
  ShipProjectile(float x, float y){
    super(x,y);
    xVelocity=0;
    yVelocity=-height/divisions*3/4;
  }
}

The logic is the same for each, the child simply gives numbers

Last class is enemy, walks down the screen from side to side.

class Enemy{
  float x;
  float y;
  float r;
  float xVelocity;
  int direction;
  Game game;
  Enemy(float x, float y, Game game){
    this.game=game;
    this.x=x;
    this.y=y;
    r=sqrt(width*height/(divisions*divisions));
    xVelocity=r/2;
    direction=1;
  }
  void update(){
    if(r<x&&x<width-r||direction==0){
      if(direction==0){
        if(x<width/2){
          direction=1;
        }
        else{
          direction=-1;
        }
      }
      x+=xVelocity*direction;
    }
    else if(x<=r&&direction!=0){
      y+=2*r;
      direction=0;
    }
    else if(x>=width-r&&direction!=0){
      y+=2*r;
      direction=0;
    }      
  }
  void draw(){
    fill(255);
    ellipse(x,y,r,r);
  }
  float getX(){
    return x;
  }
  float getY(){
    return y;
  }
  float getR(){
    return r;
  }
  boolean reachedEnd(Ship ship){
    return y>ship.getY();
  }
}

End result: 

Recreate Graphic Design

Random Squares by Bill Kolomyjec

Initially starting with this project I picked a different image, but struggled with it, so I switched to this, simpler one. That was  

And I like the effect somewhat, but I realized that each unit was not actually independent. I therefore switched to a very similar graphic where each unit was entirely independent.

int s= 100;
//int p=s/5;
void setup(){
  //size(500,500); 
  fullScreen();
  for(int x=0; x<width; x+=s){
    for(int y=0; y<height; y+=s){
      drawSquares(x,y);
    }
  }
}
void drawSquares(int x, int y){
  int xc=int(random(s/5,3*s/5));
  int yc=int(random(s/5,3*s/5));
  int num=int(random(1,11));
  int xs=xc/num;
  int ys=yc/num;
  int p= s/num;
  noFill();
  stroke(0);
  rect(x,y,s,s);
  for(int i=1; i<num; i++){
    //rect(x+i*xc/num,y+i*yc/num,x+i*(x+xc)/num,y+i*(y+yc)/num);//I like it
    rect(x+xs*i,y+ys*i,(p*(num-i)+s/5)*4/5,(p*(num-i)+s/5)*4/5);
  }
  rect(x+xs*(num),y+ys*(num),s/5*4/5,s/5*4/5);
}

I am not sure that all of the offsets (like the factor of 4/5) are quite right, but the effect is satisfactory.

Self Portrait Documentation

For this project I was rather unexcited by the idea of drawing a very low resolution image poorly, and to produce something actually interesting would require a lot of tedious measuring, or so I thought …

import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
PImage img;
BufferedImage bimage;
File f;
int[][] data= new int[1367][1367];
int[][] rdata= new int[1367][1367];
int[][] gdata= new int[1367][1367];
int[][] bdata= new int[1367][1367];
void setup(){
  size(456,456);
  img= loadImage("img.jpg");
  bimage= null;
  f= null;
  convertImage();
  noStroke();
}
void convertImage(){
  try{
    
    f = new File("img.jpg");
    System.out.println(f);
    bimage = new BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB);
    bimage = ImageIO.read(f);
    for(int x= 0; x< img.width; x++){
      for(int y= 0; y< img.height; y++){
        int rgb= bimage.getRGB(x,y);
        int red = (rgb >> 16) & 0xFF;
        int green = (rgb >> 8) & 0xFF;
        int blue = rgb & 0xFF;
        data[x][y]=rgb;
        rdata[x][y]=red;
        gdata[x][y]=green;
        bdata[x][y]=blue;
      }
    }
  }
  catch(IOException e){
  }
}
void draw(){
  int param= 18;//for best results this is a multiple of 3
  for(int x= 0; x<img.width; x+=param){
    for(int y=0; y<img.height; y+=param){
      int red= 0;
      int green= 0;
      int blue= 0;
      for(int xacc= x; xacc<img.width&&xacc<(param+x); xacc++){
        for(int yacc= y; yacc<img.height&&yacc<(param+y); yacc++){
          red+=rdata[xacc][yacc];
          green+=gdata[xacc][yacc];
          blue+=bdata[xacc][yacc];
        }
      }
      fill(red/(param*param),green/(param*param),blue/(param*param));
      rect(x/3,y/3,(x+param)/3,(y+param)/3);
    }
  }
      
        
}

What I did instead was read in my profile picture from facebook, convert it into arrays of the rgb values, and draw squares with the average value of certain blocks. I can manipulate the block sizes to change the level of pixilation …(side lengths of 3,6,and 10)

And make even more fun changes, like negative and swapping colors(blue and green in this case)

Lev Manovich Response

I will admit, I find this almost painful to read. The author is extraordinarily dramatic about points that seems far less relevant to me. Begging with his short history of new media, I find that the relationship between the development of computers and new media is far more mundane than he makes out. It seems to me that it is simply one instance of the development of computing, which is a fundamentally universal function, enveloping another discipline. New media was certainly revolutionized, and at the time it was a significant breakthrough for computing, but no task a computer performs should be surprising in type, the only surprise is when the developers figure out the software to achieve some particular degree- this is the nature of a universal process.

I also find that the distinction between new and old media he makes in the very first principle is misleading. Specifically, what is new about new media is not that it can be described mathematically, but that the media was created with this knowledge in mind. Photography and sculpture are far from continuous, but this knowledge is not relevant from the majority of people who use it. However, I have experience with a realm of photography where light levels are so low that the image is considered in terms of individual photon counts: astronomy.

What I am basically getting at is that I strongly disagree with the implication made that new media is fundamentally new- it is our understanding of media that is new, the tools and processes differ only superficially. (I could go on, this response has only responded to the first couple pages, but I think I have made my point)

Casey Reas’ Eyeo talk Response

While this talk contained a large number of very interesting random based artworks, I felt that it lacked a point. While it was stated that different balances of randomness and control explore different concepts, there was little analysis of this point. I would like to do this analysis myself, but the reason it is so difficult is because fundamentally what was described is a tool, and the primary purpose of of the talk seems to be to emphasize what the randomness itself brings to art, even when done with simple graphics. I personally felt that the most interesting and aesthetic pieces were where the algorithms were the most sophisticated, as the results had a geometric appearance at each static moment but moved very organically. However, as I mentioned this juxtaposition was under-analyzed, and mostly this was just a series of very cool images and some hint of the underlying algorithms, at least to me.