# Linear compass display to use with a MPU

**URL:** <https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060>\
**Category:** Electronics (Arduino, etc.)\
**Created:** [March 9, 2026, 8:18pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060 "2026-03-09T20:18:02Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 24, 2026, 10:34pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/22 "2026-03-24T22:34:35Z")

</div>

Hello @philtkp ,

I found my BNO055!

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/2/7/27e84eb4fba3210ee74e854dc389a6ff9324b982.jpeg)

Still works like a charm!

The bottom right is not connected… I think I had Bluetooth connected at one time.

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 25, 2026, 1:26am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/23 "2026-03-25T01:26:50Z")

</div>

@glv

Sweet!, I’m using the Sparkfun RedBoard and the GY80. They were a bit dusty but fired right up.

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 25, 2026, 3:43pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/24 "2026-03-25T15:43:00Z")

</div>

@glv Now that i started using the GY80 vice the mouse i noticed that when i rotate the board CLOCK WISE the displayed readings go COUNTER CLOCK WISE. cant seem to correct that.😫

> **CODE**
>
> ```auto
> 
> // LINEAR COMPASS
> import processing.serial.*;
> Serial fd;
> 
> PGraphics hdg;
> PGraphics comp;
> PGraphics ahm;
> 
> PImage fade;
> 
> float Pitch;
> float Bank;
> float Azimuth;
> float degree1;
> //float yaw = 0.0f;
> float yawOffset = 0.0f;
> float SpanAngle=120;
> //float GaugeWidth=304;  
> //float StrokeWidth=1;
> //float an;
> //float DivxPhasorCloser;
> //float DivxPhasorDistal;
> //float DivyPhasorCloser;
> //float DivyPhasorDistal;
> int W=300; //My Laptop's screen width
> int H=300; 
> int degree = 0; 
> int heading = 0;
> int pitch = 0;
> int roll = 0;
> int yaw = 0;
> int yawDeg = 0;
> int rollDeg = 0;
> int pitchDeg = 0 ;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions;
> PVector v1, v2; 
> //
> // ---------------------------------------------------------------
> //
> void setup()
> {
> // init
> size(800, 800, P2D);
> hdg = createGraphics(300,50);
> comp = createGraphics(300,300);
> ahm = createGraphics(300,300);
>   
> fade = loadImage("fade2.png");
>   
> fd = new Serial(this, Serial.list()[1], 115200);
> // Defer callback until new line
> fd.bufferUntil('\n');
> rectMode(CENTER);
> smooth();
> strokeCap(SQUARE);//Optional
> //background(0);
> } // func 
>  
> void draw() 
> { 
>  
>  
>  
> background(0);
> LCompass();
> Compass();
> drawHorz();
>  
> println("PITCH: "+pitch," ROLL: "+roll,"YAW: "+yaw);
> //println("Linear compass : DEGREE = "+ degree," YAW DEGREE = "+ (360-(yawDeg%360)));
>   
> }
> 
> void serialEvent (Serial fd) 
> {
> // get the ASCII string:
> String rpstr = fd.readStringUntil('\n');
> if (rpstr != null) {
> String[] list = split(rpstr, ':');
> pitch = ((int)float(list[0]));
> roll = ((int)float(list[1]));
> yaw = ((int)float(list[2]));
>         
> }
> }
> 
> / ********************************************************** /
> /* DRAW LINEAR COMPASS WITH FADE
> / ********************************************************** /
> void LCompass(){
>  
> 
> hdg.beginDraw();
> hdg.background(255);
>  
> hdg.image(fade,300,50);
> // black rect (the whole compass)
> hdg.noFill();
> hdg.stroke(0); //Black
> hdg.rect(0, 0, 299, 49);
>   
> // yellow rect (the display)  
> hdg.fill(255,255,2);
> hdg.rect(0, 25, 299, 24);
>   
> // yellow triangle 
>    
> hdg.stroke(0);// black
> hdg.fill(255, 255, 2); // yellow 
>   
> hdg.triangle(150-5, 18, 150+5, 18, 150, 24);
>  
> int degree = 360-(yaw%360);
>   
> // DISPLAY DEGREES
> hdg.stroke(0);// black
> hdg.fill(0); // black 
> hdg.textAlign(CENTER);
> hdg.textSize(16);
> hdg.text(yawDeg,150, 15); 
>  
> // DRAW BLACK DEGREE MARKS
> hdg.stroke(0); // black
> int lengthLine = 0;  
> for (int i=0; i < 360; i+=5)
> {
> if (i%90==0) { 
> lengthLine=9;
> } 
> else if(i%45==0){
> lengthLine=6;
> }
> else
> {
> lengthLine=3;
> }
> hdg.line(((degree-30)+i)%360, 26, ((degree-30)+i)%360, 26+lengthLine);
> hdg.fill(0);
>   
> }
> hdg.textSize(14);
> hdg.text("S", ((degree-30)+360)%360, 47);
> hdg.text("N", ((degree-30)+180)%360, 47);
> hdg.text("E", ((degree-30)+270)%360, 47);
> hdg.text("W", ((degree-30)+450)%360, 47);
> hdg.textSize(10);
> hdg.text("NW", ((degree-30)+495)%360, 47);
> hdg.text("NE", ((degree-30)+225)%360, 45);
> hdg.text("SE", ((degree-30)+315)%360, 45);
> hdg.text("SW", ((degree-30)+405)%360, 45);
>  
> // draw red line in display
> hdg.stroke(255, 2, 2); 
> hdg.line(150, 26, 150, 48);
>   
> println("yaw: ",+yaw," yawDeg:",+yawDeg," degree: ",degree%360);
>   
> hdg.image(fade,0,0); 
> hdg.endDraw();
> image(hdg, 280, 320);
> }
>  
> / ********************************************************** /
> /* DRAW DIAL COMPASS
> / ********************************************************** /
> void Compass(){
> comp.beginDraw();
> comp.background(0);
>  
> yaw = (int)map(yaw, 0, 360, 0, 360);
> yawDeg = (int)(yaw%360);
>   
> comp.pushMatrix();
> comp.noStroke();
> comp.fill(108, 156, 255); // Fill sky color
> comp.ellipse(150, 150, 220, 220);
>  
> if((int)yaw >= 1 && (int)yaw <= 180) // Keeps numbers positive
> yawDeg = (int)yaw;
> else yawDeg = 360 + (int)yaw;
> 
> // DRAW DEGREES INSIDE COMPASS 
> comp.noFill();
> comp.stroke(255); //Black
> comp.rect(125, 222, 50, 30);
> comp.fill(0); //Black
> comp.rect(125, 222, 50, 30);
> comp.textSize(20);
> comp.fill(255,255,255);
> comp.textAlign(CENTER);
> comp.text(yawDeg, 150, 245); 
>  
> comp.translate(150, 150);   
>   
> CompassPointer();
>   
> comp.popMatrix();
> comp.pushMatrix();
>   
> /* Draws the circular scale on compass */
> comp.translate(150, 150); 
> comp.stroke(255);
> comp.strokeWeight(2);
> comp.noFill();
> comp.ellipse(0, 0, 220, 220);
>    
> SpanAngle=180;
> NumberOfScaleMajorDivisions=18;
> NumberOfScaleMinorDivisions=36; 
>    
> CircularScale(); 
> 
> comp.rotate(PI);
> SpanAngle=180;  
>    
> CircularScale();
> comp.rotate(-PI);
>    
> comp.translate(-300, 100);
> comp.textSize(20);
> comp.fill(255,255,255);
> comp.textAlign(CENTER);
> comp.text("W", 165, -92);
> comp.text("E", 430, -92);
> comp.text("N", 300, -230);
> comp.text("S", 300, 42);
> comp.rotate(PI/4);
> comp.textSize(15);
> comp.text("NW",-0, -280);
> comp.text("SE", 275, -280);
> comp.popMatrix();
> comp.rotate(-PI/4);
> comp.text("NE",140,220);
> comp.text("SW",-140, 220);
>    
> comp.endDraw();
> image(comp, 280, 10);
> }
> 
> / ********************************************************** /
> /* DRAW ROLL/PITCH INDICATOR */
> / ********************************************************** /
> void drawHorz() {
>   
> ahm.beginDraw();
> ahm.translate(0,0);  
>  
> // int pitchDeg = 360-(pitch%360);
> roll = (int)map(roll, 0, 360, 0, 360);
> rollDeg = (int)((roll%360));
> 
> //if((int)Pitch >= 1 && (int)Pitch <= 180)
> // pitchDeg = (int)pitch;
> // else pitchDeg = 360 + (int)Pitch;
>  
> Pitch = (int)map(pitch, 0, 360, 0, 360);
> pitchDeg = (int)((Pitch%360));
>  
> //println("BANK: ",+rollDeg," PITCH: ",+pitchDeg);
> //println("pitch: ",+pitch," Pitch:",+Pitch," pitchDeg: ",(pitchDeg%360));
>   
> ahm.noStroke();
> ahm.fill(0, 180, 255);//fills sky
> ahm.rect(0, 0, 300, 300);
> ahm.fill(95, 55, 40); //Fills ground
>   
> ahm.rotate(-rollDeg);
> ahm.rect(0, 150+pitchDeg%360, 300, 300); 
> ahm.translate(150,150);
> ahm.push();
> ahm.rotate(rollDeg);
> ahm.rotate(-PI-PI/6);
>  
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;  
>  
> CircularScale2(); 
> ahm.rotate(PI+PI/6);
> ahm.rotate(-PI/6);  
> CircularScale2();
> ahm.rotate(PI/6);
> ahm.rotate(-rollDeg%360); 
>   
>   
> PitchScale();
> Axis(); 
> ahm.pop();
> ahm.rotate(rollDeg%360);
> ahm.endDraw();
> image(ahm, 280, 390);
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale()
> {
> float GaugeWidth=304;  
> comp.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> comp.strokeWeight(2*StrokeWidth);
> comp.stroke(255);
> comp.noFill();
>    
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));  
>      
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> comp.strokeWeight(5);
> comp.stroke(255);
>       
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> comp.strokeWeight(1);
> comp.stroke(100, 255, 100);
>       
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> comp.strokeWeight(3);
> comp.stroke(255);
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale2()
> {
> float GaugeWidth=304;  
> ahm.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> ahm.strokeWeight(2*StrokeWidth);
> ahm.stroke(255);
> ahm.noFill();
>    
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));  
>      
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> ahm.strokeWeight(5);
> ahm.stroke(255);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> ahm.strokeWeight(1);
> ahm.stroke(100, 255, 100);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> ahm.strokeWeight(3);
> ahm.stroke(255);
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> / *************************************************** /
> /* Draws the compass pointer */
> / *************************************************** / 
> void CompassPointer()
> {
> comp.rotate(radians(yaw));  
> comp.stroke(0);
> //strokeWeight(1);
> comp.fill(255,255,255);
>   
> comp.beginShape();
> comp.vertex(0,-105);
> comp.vertex(12, 100);
> comp.vertex(0, 80);
> comp.vertex(-12, 100);
> comp.endShape(CLOSE);
>   
> comp.ellipse(0, 0, 9, 9);  
> comp.ellipse(0, 0, 3, 3); 
> }
> / *************************************************** /
> /* Draw the pitch scale */
> / *************************************************** /
> void PitchScale()
> { 
>   
> //ahm.push();
> //ahm.translate(0,0);
>   
> ahm.stroke(255);
> ahm.fill(255);
> ahm.strokeWeight(2);
> ahm.textSize(15);
> ahm.textAlign(CENTER,CENTER);
> for (int i=-4;i<5;i++)
> {  
> if ((i==0)==false)
> {
> ahm.line(40, 20*i, -40, 20*i);
> }  
> ahm.text(""+i*20, 20, 20*i, -150, 20);
> ahm.text(""+i*20, -20, 20*i, 150, 20);
> }
> //textAlign(CORNER);
> ahm.strokeWeight(2);
> for (int i=-9;i<10;i++)
> {  
> if ((i==0)==false)
> {    
> ahm.line(20, 10*i, -20, 10*i);
> }
> }
> //ahm.pop();
> }
> 
> / *************************************************** /
> /* Draw red axis lines */
> / *************************************************** /
> void Axis()
> {
> ahm.stroke(255,0,0);
> ahm.strokeWeight(2);
> ahm.line(-45, 0, 45, 0);
> ahm.line(0, 90, 0, -90);
> ahm.fill(100, 255, 100); //GREEN
> ahm.stroke(0);
> ahm.strokeWeight(1);
> ahm.triangle(0, -107, -6, -90, 6, -90);
> ahm.triangle(0, 107, -6, 90, 6, 90);
> } 
> 
> ```

![fade2](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/1/b/1b6f759a01ebdf997d8fc07f4ba40971d7cf5d16.png) This is the fade i used to give the linear compass a some what 3d look.

I’m going to see if i can take the kinks of the roll and pick indicator. Time to put on another pot of coffee.

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 25, 2026, 7:12pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/25 "2026-03-25T19:12:28Z")

</div>

Hello @philtkp,

Wrap your head around this (no pun intended):

```processing
int wrap360(int a)
  {
  return (a%360 + 360)%360;
  }

void draw() 
{  
 // N is width/2 for mouseX
 // This works for any negative value (may be excessive here):
 // yaw = ((int) map(mouseX, 00, width, -180, 180)%360+360)%360; // handles negatives with %
 
 // Cleaner presentation of above:
 int yawMap = (int) map(mouseX, 00, width, -180, 180);// handles negatives with % 
 yaw = wrap360(yawMap);
 
 // This works for your case:
 yaw = ((int) map(mouseX, 00, width, -180, 180)+360)%360; // handles negatives with %
 
 // Also works for 0 to 360
 //yaw = (int) map(mouseX, 0, width, 0, 360)%360;
 
 background(0);
 LCompass();
 Compass();
 drawHorz();
 
 fill(255);
 textSize(24);
 text(yaw, 20, 30); 
 
 println("PITCH: "+pitch," ROLL: "+roll,"YAW: "+yaw);
//println("Linear compass : DEGREE = "+ degree," YAW DEGREE = "+ (360-(yawDeg%360)));  
}

```

Generalized wrap function for any int value:

```auto
int wrap360(int a)
  {
  return (a%360 + 360)%360;
  }

```

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 26, 2026, 1:23am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/26 "2026-03-26T01:23:54Z")

</div>

ok, just have a 2 brain cell moment!😯 i had the board with the smd parts facing up when it should of been down. grabbed one that had the pins soldered on the other side and it works fine. aaAAHHHH.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/d/2/d2d017811802c4697ea320b46b8949103dd173da.jpeg)

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 27, 2026, 4:22am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/27 "2026-03-27T04:22:24Z")

</div>

@glv I’m working on the roll/pitch indicator. I’m doing the pitch part first.

have a problem with the pitch scale degrees. I’m unable to align it to zero when using PGraphics

its in drawHorz() and PitchScale(). I think its this part of the code  
`ahm.text(""+i*20, 20, 20*i, -150, 20); // draws left side degrees`  
` ahm.text(""+i*20, -20, 20*i, 150, 20); // draws right side degrees`

> **CODE USING PGRAPHICS**
>
> ```auto
> 
> // LINEAR COMPASS
> import processing.serial.*;
> Serial fd;
> 
> PGraphics hdg;
> PGraphics comp;
> PGraphics ahm;
> 
> PImage fade;
> 
> float Pitch;
> float Bank;
> float Azimuth;
> float degree1;
> //float yaw = 0.0f;
> float yawOffset = 0.0f;
> float SpanAngle=120;
> //float GaugeWidth=304;  
> //float StrokeWidth=1;
> //float an;
> //float DivxPhasorCloser;
> //float DivxPhasorDistal;
> //float DivyPhasorCloser;
> //float DivyPhasorDistal;
> int W=300; //My Laptop's screen width
> int H=300; 
> int degree = 0; 
> int heading = 0;
> int pitch = 0;
> int roll = 0;
> int yaw = 0;
> int yawDeg = 0;
> int rollDeg = 0;
> int pitchDeg = 0 ;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions;
> PVector v1, v2; 
> //
> // ---------------------------------------------------------------
> //
> void setup()
> {
> // init
> size(800, 800, P2D);
> hdg = createGraphics(300,50);
> comp = createGraphics(300,300);
> ahm = createGraphics(300,300);
>   
> fade = loadImage("fade2.png");
>   
> fd = new Serial(this, Serial.list()[1], 115200);
> // Defer callback until new line
> fd.bufferUntil('\n');
> rectMode(CENTER);
> smooth();
> strokeCap(SQUARE);//Optional
> //background(0);
> } // func 
>  
> void draw() 
> { 
>  
>  
>  
> background(0);
> LCompass();
> Compass();
> drawHorz();
>  
> println("PITCH: "+pitch," ROLL: "+roll,"YAW: "+yaw);
> //println("Linear compass : DEGREE = "+ degree," YAW DEGREE = "+ (360-(yawDeg%360)));
>   
> }
> 
> void serialEvent (Serial fd) 
> {
> // get the ASCII string:
> String rpstr = fd.readStringUntil('\n');
> if (rpstr != null) {
> String[] list = split(rpstr, ':');
> pitch = ((int)float(list[0]));
> roll = ((int)float(list[1]));
> yaw = ((int)float(list[2]));
>         
> }
> }
> 
> / ********************************************************** /
> /* DRAW LINEAR COMPASS WITH FADE
> / ********************************************************** /
> void LCompass(){
>  
> 
> hdg.beginDraw();
> hdg.background(255);
>  
> hdg.image(fade,300,50);
> // black rect (the whole compass)
> hdg.noFill();
> hdg.stroke(0); //Black
> hdg.rect(0, 0, 299, 49);
>   
> // yellow rect (the display)  
> hdg.fill(255,255,2);
> hdg.rect(0, 25, 299, 24);
>   
> // yellow triangle 
>    
> hdg.stroke(0);// black
> hdg.fill(255, 255, 2); // yellow 
>   
> hdg.triangle(150-5, 18, 150+5, 18, 150, 24);
>  
> int degree = 360-(yawDeg%360);
>  
>  
> // DISPLAY DEGREES
> hdg.stroke(0);// black
> hdg.fill(0); // black 
> hdg.textAlign(CENTER);
> hdg.textSize(16);
> hdg.text(yawDeg%360,150, 15); 
>  
> // DRAW BLACK DEGREE MARKS
> hdg.stroke(0); // black
> int lengthLine = 0;  
> for (int i=0; i < 360; i+=5)
> {
> if (i%90==0) { 
> lengthLine=9;
> } 
> else if(i%45==0){
> lengthLine=6;
> }
> else
> {
> lengthLine=3;
> }
> hdg.line(((degree-30)+i)%360, 26, ((degree-30)+i)%360, 26+lengthLine);
> hdg.fill(0);
>   
> }
> hdg.textSize(14);
> hdg.text("S", ((degree-30)+360)%360, 47);
> hdg.text("N", ((degree-30)+180)%360, 47);
> hdg.text("E", ((degree-30)+270)%360, 47);
> hdg.text("W", ((degree-30)+450)%360, 47);
> hdg.textSize(10);
> hdg.text("NW", ((degree-30)+495)%360, 47);
> hdg.text("NE", ((degree-30)+225)%360, 45);
> hdg.text("SE", ((degree-30)+315)%360, 45);
> hdg.text("SW", ((degree-30)+405)%360, 45);
>  
> // draw red line in display
> hdg.stroke(255, 2, 2); 
> hdg.line(150, 26, 150, 48);
>   
> hdg.image(fade,0,0); 
> hdg.endDraw();
> image(hdg, 280, 320);
> }
>  
> / ********************************************************** /
> /* DRAW DIAL COMPASS
> / ********************************************************** /
> void Compass(){
> comp.beginDraw();
> comp.background(0);
>  
> yaw = (int)map(yaw, 0, 360, 0, 360);
> yawDeg = (int)(yaw%360);
>   
> comp.pushMatrix();
> comp.noStroke();
> comp.fill(108, 156, 255); // Fill sky color
> comp.ellipse(150, 150, 220, 220);
>  
> println("dial compass " + yaw, yawDeg);
>  
> if((int)yaw >= 1 && (int)yaw <= 180)
> yawDeg = (int)yaw;
> else yawDeg = 360 + (int)yaw;
> 
> // DRAW DEGREES INSIDE COMPASS 
> comp.noFill();
> comp.stroke(255); //Black
> comp.rect(125, 222, 50, 30);
> comp.fill(0); //Black
> comp.rect(125, 222, 50, 30);
> comp.textSize(20);
> comp.fill(255,255,255);
> comp.textAlign(CENTER);
> comp.text(yawDeg%360, 150, 245); 
>  
> comp.translate(150, 150);   
>   
> CompassPointer();
>   
> comp.popMatrix();
> comp.pushMatrix();
>   
> /* Draws the circular scale on compass */
> comp.translate(150, 150); 
> comp.stroke(255);
> comp.strokeWeight(2);
> comp.noFill();
> comp.ellipse(0, 0, 220, 220);
>    
> SpanAngle=180;
> NumberOfScaleMajorDivisions=18;
> NumberOfScaleMinorDivisions=36; 
>    
> CircularScale(); 
> 
> comp.rotate(PI);
> SpanAngle=180;  
>    
> CircularScale();
> comp.rotate(-PI);
>    
> comp.translate(-300, 100);
> comp.textSize(20);
> comp.fill(255,255,255);
> comp.textAlign(CENTER);
> comp.text("W", 165, -92);
> comp.text("E", 430, -92);
> comp.text("N", 300, -230);
> comp.text("S", 300, 42);
> comp.rotate(PI/4);
> comp.textSize(15);
> comp.text("NW",-0, -280);
> comp.text("SE", 275, -280);
> comp.popMatrix();
> comp.rotate(-PI/4);
> comp.text("NE",140,220);
> comp.text("SW",-140, 220);
>    
> comp.endDraw();
> image(comp, 280, 10);
> }
> 
> / ********************************************************** /
> /* DRAW ROLL/PITCH INDICATOR */
> / ********************************************************** /
> void drawHorz() {
>   
> ahm.beginDraw();
> // ahm.translate(0,0);  
>  
> // int pitchDeg = 360-(pitch%360);
> //Bank = (int)map(roll, 0, 360, 0, 360);
> //rollDeg = (int)((Bank%360));
> 
> //if((int)Pitch >= 1 && (int)Pitch <= 180)
> // pitchDeg = (int)pitch;
> // else pitchDeg = 360 + (int)Pitch;
>  
> Pitch = (int)map(pitch, 0, 360, 0, 360);
> pitchDeg = (int)((Pitch%360));
>  
> println("BANK: ",+rollDeg," PITCH: ",+pitchDeg);
> println("pitch: ",+pitch," Pitch:",+Pitch," pitchDeg: ",(pitchDeg%360));
>   
> ahm.noStroke();
> ahm.fill(0, 180, 255);//fills sky
> ahm.rect(0, 0, 300, 300);
> ahm.fill(95, 55, 40); //Fills ground
>   
> //ahm.rotate(-rollDeg);
> ahm.rect(0, 150-pitchDeg%360, 300, 300); 
> ahm.translate(150,150); // Places pitch scale center
> //ahm.push();
> //ahm.rotate(rollDeg);
> ahm.rotate(-PI-PI/6); // Centers upper scale
>  
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;  
>  
> CircularScale2(); //Draws upper scale
> ahm.rotate(PI+PI/6); // Centers lower scale
> ahm.rotate(-PI/6); //
> CircularScale2(); //Draws lower scale
> ahm.rotate(PI/6); // Aligns pitch scale and axis
> //ahm.rotate(-rollDeg%360); 
>   
>   
> PitchScale();
> Axis(); 
> //ahm.pop();
> //ahm.rotate(rollDeg%360);
> ahm.endDraw();
> image(ahm, 280, 390);
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale()
> {
> float GaugeWidth=304;  
> comp.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> comp.strokeWeight(2*StrokeWidth);
> comp.stroke(255);
> comp.noFill();
>    
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));  
>      
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> comp.strokeWeight(5);
> comp.stroke(255);
>       
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> comp.strokeWeight(1);
> comp.stroke(100, 255, 100);
>       
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> comp.strokeWeight(3);
> comp.stroke(255);
> comp.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale2()
> {
> float GaugeWidth=304;  
> ahm.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> ahm.strokeWeight(2*StrokeWidth);
> ahm.stroke(255);
> ahm.noFill();
>    
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));  
>      
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> ahm.strokeWeight(5);
> ahm.stroke(255);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> ahm.strokeWeight(1);
> ahm.stroke(100, 255, 100);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> ahm.strokeWeight(3);
> ahm.stroke(255);
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> / *************************************************** /
> /* Draws the compass pointer */
> / *************************************************** / 
> void CompassPointer()
> {
> comp.rotate(radians(yaw));  
> comp.stroke(0);
> //strokeWeight(1);
> comp.fill(255,255,255);
>   
> comp.beginShape();
> comp.vertex(0,-105);
> comp.vertex(12, 100);
> comp.vertex(0, 80);
> comp.vertex(-12, 100);
> comp.endShape(CLOSE);
>   
> comp.ellipse(0, 0, 9, 9);  
> comp.ellipse(0, 0, 3, 3); 
> }
> / *************************************************** /
> /* Draw the pitch scale */
> / *************************************************** /
> void PitchScale()
> { 
>   
> //ahm.push();
> //ahm.translate(0,0);
>   
> ahm.stroke(255);
> ahm.fill(255);
> ahm.strokeWeight(2);
> ahm.textSize(14);
> ahm.textAlign(CENTER,CENTER);
> for (int i=-4;i<5;i++)
> {  
> if ((i==0)==false)
> {
> ahm.line(40, 20*i, -40, 20*i); // Draws wider lines
> }  
> ahm.text(""+i*20, 20, 20*i, -150, 20); // draws left side degrees
> ahm.text(""+i*20, -20, 20*i, 150, 20); // draws right side degrees
> }
> //textAlign(CORNER);
> ahm.strokeWeight(1);
> for (int i=-9;i<10;i++)
> {  
> if ((i==0)==false)
> {    
> ahm.line(20, 10*i, -20, 10*i); // Draws narrower lines
> }
> }
> //ahm.pop();
> }
> 
> / *************************************************** /
> /* Draw red axis lines */
> / *************************************************** /
> void Axis()
> {
> ahm.stroke(255,0,0);
> ahm.strokeWeight(2);
> ahm.line(-45, 0, 45, 0);
> ahm.line(0, 90, 0, -90);
> ahm.fill(100, 255, 100); //GREEN
> ahm.stroke(0);
> ahm.strokeWeight(1);
> ahm.triangle(0, -107, -6, -90, 6, -90);
> ahm.triangle(0, 107, -6, 90, 6, 90);
> } 
> 
> ```

but works without using it.

> **CODE WITHOUT USING PGRAPHICS**
>
> ```auto
> //Artificial Horizon+Compass by Adrian Fernandez 4-19-2013
> //Built with Processing 1.5.1
> int W=300; //My Laptop's screen width
> int H=300; //My Laptop's screen height
> float Pitch;
> float Bank;
> float Azimuth;
> float ArtificialHoizonMagnificationFactor=0.7;
> float CompassMagnificationFactor=0.85;
> float SpanAngle=120;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions; 
> PVector v1, v2; //For testing only
> 
> void setup()
> {
> size(300, 300); 
> rectMode(CENTER);
> smooth();
> //strokeCap(SQUARE);//Optional
> }
> 
> void draw()
> {
> background(0); 
> //
> MakeAnglesDependentOnMouse();
>   
> Horizon();
>   
> //Borders();
> //Plane();
> ShowAngles(); 
>   
> }
> 
> void MakeAnglesDependentOnMouse() //For testing only.
> { 
> v2= new PVector(); 
> v1= new PVector(W/2, H/2);
> v2.x=mouseX;
> v2.y=mouseY;
> Bank = PVector.angleBetween(v1, v2);
> Pitch=mouseY-H;
> Azimuth=(180/PI*10*Bank)%360;
> }
> 
> void Horizon()
> {
> translate(150, 150);  
>   
>   
> noStroke();
> fill(0, 180, 255);//fills sky
> rect(2, 2, 298, 298);
> fill(95, 55, 40); //Fills ground
> rotate(-Bank);
> rect(0, 300+Pitch, 298, 298); 
> rotate(Bank);
> rotate(-PI-PI/6);
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;  
> CircularScale(); 
> rotate(PI+PI/6);
> rotate(-PI/6);  
> CircularScale();
> rotate(PI/6);
> rotate(-Bank); 
> PitchScale();
> Axis(); 
> rotate(Bank);
> }
> 
> void Plane()
> {
> fill(0);
> strokeWeight(1);
> stroke(0, 255, 0);
> triangle(-20, 0, 20, 0, 0, 25); 
> rect(110, 0, 140, 20);
> rect(-110, 0, 140, 20);
> }
> 
> void CircularScale()
> {
> float GaugeWidth=304;  
> textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> strokeWeight(2*StrokeWidth);
> stroke(255);
> noFill();
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));   
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> strokeWeight(5);
> stroke(0);
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> strokeWeight(1);
> stroke(100, 255, 100);
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> strokeWeight(3);
> stroke(255);
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> void Axis()
> {
> stroke(255, 0, 0);
> strokeWeight(2);
> line(-45, 0, 45, 0);
> line(0, 90, 0, -90);
> fill(100, 255, 100);
> stroke(0);
> triangle(0, -107, -6, -90, 6, -90);
> triangle(0, 107, -6, 90, 6, 90);
> } 
> 
> void ShowAngles()
> {
> textSize(30);
> fill(50);
> noStroke();
> rect(-150, 400, 280, 40);
> rect(150, 400, 280, 40);
> fill(255); 
> Pitch=Pitch/5;
> int Pitch1=round(Pitch); 
> Bank=Bank*180/PI;
> int Bank1=round(Bank);
> text("Pitch: "+Pitch1+" Deg", -20, 411, 500, 60);
> text("Bank: "+Bank1+" Deg", 280, 411, 500, 60);
> }
> 
> void Borders()
> {
> noFill();
> stroke(0);
> strokeWeight(400);
> rect(0, 0, 1100, 1100);
> strokeWeight(200);
> ellipse(0, 0, 1000, 1000); 
> fill(0);
> noStroke();
> rect(4*W/5, 0, W, 2*H);
> rect(-4*W/5, 0, W, 2*H);
> }
> 
> void PitchScale()
> {  
> stroke(255);
> fill(255);
> strokeWeight(2);
> textSize(15);
> textAlign(CENTER,CENTER);
> for (int i=-4;i<5;i++)
> {  
> if ((i==0)==false)
> {
> line(40, 20*i, -40, 20*i);
> }  
> text(""+i*20, 60, 20*i, 100, 30);
> text(""+i*20, -60, 20*i, 100, 30);
> }
> //textAlign(CORNER);
> strokeWeight(2);
> for (int i=-9;i<10;i++)
> {  
> if ((i==0)==false)
> {    
> line(20, 10*i, -20, 10*i);
> }
> }
> } 
> 
> ```

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 27, 2026, 8:27pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/28 "2026-03-27T20:27:48Z")

</div>

Hello @philtkp,

This aligned the numbers to the scale:

```auto
    ahm.text(""+i*20, 20, 20*i -10, -150, 20); // draws left side degrees   
    ahm.text(""+i*20, -20, 20*i -10, 150, 20); // draws right side degrees

```

I do not often use the text box so had to look that up!

Reference:  
_[text() / Reference / Processing.org](https://processing.org/reference/text_.html)_

This also worked on my end:

```processing
  pitch = ((int) map(mouseX, 00, width, -95, 95));
  pitch = constrain(pitch, -90, 90);
  
  int ym = 160-90; // y margin
  pitchDeg = (int) map(pitch, -90, 90, ym, 300- ym); // You want this mapped to the scale
  println(pitch, pitchDeg);
  
  ahm.noStroke();
  ahm.fill(0, 180, 255);//fills sky
  ahm.rect(0, 0, 300, 300);
  ahm.fill(95, 55, 40); //Fills ground
  
  ahm.rect(0, 0, 300, pitchDeg);

```

What data is being received for yaw, pitch and roll?  
Helps on my end to simulate with mouse or otherwise.  
I like to simulate!

> [@How to draw real-time data in a specific canvas area？](https://discourse.processing.org/t/how-to-draw-real-time-data-in-a-specific-canvas-area/45631/5):
>
> Hello @cola, Another example with simulated data in a thread: // Draw data generated in a thread in realtime. // Data is buffered and drawn with each frame. // // Author: GLV // Date: 2025-01-22 PGraphics d0; FloatList data0; int i; void setup() { size(600, 500); d0 = createGraphics(400, 100); data0 = new FloatList(); frameRate(60); // default thread("requestData"); // start thread to generate data } //\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*…

Have fun!

`:)`

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 28, 2026, 1:24am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/29 "2026-03-28T01:24:01Z")

</div>

Another thing you can do with PGraphics is create the image in advance in setup()

Simplified example:

```auto
float SpanAngle=120;

int NumberOfScaleMajorDivisions;
int NumberOfScaleMinorDivisions;

int dir = 1;

PImage cpd, cpp;

// Global setup
void setup() {
  // Setup graphics
  size(400, 400);

  cpd = makeCompassDial();
  cpp = makeCompassPointer();

  imageMode(CENTER);
  image(cpd, width/2, height/2);
  image(cpp, 200, 200);
}

int angleDriver; // For linear and phase

// For counter
int wrap360(int angle)
  {
  //return (count + 360) % 360; // Ok for one cycle!
  return (angle%360 + 360) % 360; // In case angle is negative!
  }

void draw()
{
  background(255);
  image(cpd, width/2, height/2);

  //angleDriver = (angleDriver + dir + 360) % 360; //dir = +1 or -1 
  angleDriver = wrap360(angleDriver + dir);
  
float angle = radians(180) * sin(radians(angleDriver)); // sinusoidal

 push();
  translate(width/2, height/2);
  rotate(angle);
  image(cpp, 0, 0);
 pop();
 
 fill(0);
 textAlign(CENTER, TOP);
 textSize(24);
 //text((int)((degrees(angle)%360 + 360) % 360), width/2, 20);
 text(wrap360((int)degrees(angle)), width/2, 20);
}

/ ********************************************************** /
/* Draw Compass Dial */
/ ********************************************************** /

PGraphics cp; // Global because I was passing a function

PImage makeCompassDial() {

  cp = createGraphics (300, 300);

  cp.beginDraw();
  cp.background(255, 0, 0, 10); // some opacity for development!
  cp.translate(450, 50);

 cp.pushMatrix();

  cp.noStroke();
  cp.fill(108, 156, 255); // Fill sky color
  cp.ellipse(-300, 100, 220, 220);

 cp.popMatrix();

 cp.pushMatrix();

  /* Draws the circular scale on compass */
  cp.translate(-300, 100);

  cp.stroke(255);
  cp.strokeWeight(2);
  cp.noFill();
  cp.ellipse(0, 0, 220, 220);

  SpanAngle=180;
  NumberOfScaleMajorDivisions=18;
  NumberOfScaleMinorDivisions=36;;

  cp.translate(-300, 100);
  cp.textSize(20);
  cp.fill(0);
  cp.textAlign(CENTER);
  cp.text("W", 165, -92);
  cp.text("E", 430, -92);
  cp.text("N", 300, -230);
  cp.text("S", 300, 42);
  cp.rotate(PI/4);
 cp.popMatrix();

  cp.endDraw();

  PImage img = cp.get();
  return img;
}

/ *************************************************** /
/* Draw Compass Pointer */
/ *************************************************** /

PImage makeCompassPointer()
{
  PGraphics cp2 = createGraphics (200, 200);

  cp2.beginDraw();
  cp2.translate(100, 100);
  cp2.stroke(0);
  strokeWeight(2);
  cp2.fill(255, 255, 255);

  cp2.triangle(0, -100, -10, 100, 10, 100);

  cp2.endDraw();

  PImage img1 = cp2.get();
  return img1;
}

void mousePressed()
  {
  dir *= -1; //chamge direction
  }

```

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 29, 2026, 12:12am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/30 "2026-03-29T00:12:31Z")

</div>

I was aware that you could do that. “SWEET”

Been trying to get the pitch working properly. I’m thing i need to map the ground rectangle. but anything tried miss aligns the pitch scale. this is what I’m trying to get

 ![PIT_ROL](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/b/e/be546fe9c3d0941ea7383d95d6b5575c676593bf.jpeg)

I know these should be at 90 increments of a 360 degree cycle.

> **CODE**
>
> ```auto
> 
> // LINEAR COMPASS
> import processing.serial.*;
> Serial fd;
> 
> PGraphics hdg;
> PGraphics comp;
> PGraphics ahm;
> PImage fade;
> 
> float Pitch;
> float Bank;
> float Azimuth;
> float degree1;
> //float yaw = 0.0f;
> float yawOffset = 0.0f;
> float SpanAngle=120;
> //float GaugeWidth=304;  
> //float StrokeWidth=1;
> //float an;
> //float DivxPhasorCloser;
> //float DivxPhasorDistal;
> //float DivyPhasorCloser;
> //float DivyPhasorDistal;
> int W=300; //My Laptop's screen width
> int H=300; 
> int degree = 0; 
> int heading = 0;
> int pitch = 0;
> int roll = 0;
> int yaw = 0;
> int yawDeg = 0;
> int rollDeg = 0;
> int pitchDeg = 0;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions;
> PVector v1, v2; 
> //
> // ---------------------------------------------------------------
> //
> void setup()
> {
> // init
> size(800, 800, P3D);
>   
> ahm = createGraphics(300,300);
> 
>     
> fd = new Serial(this, Serial.list()[1], 115200);
> // Defer callback until new line
> fd.bufferUntil('\n');
>  
> rectMode(CENTER);
> smooth();
> strokeCap(SQUARE);//Optional
> background(0);
> } // func 
>  
> void draw() 
> { 
>  
> drawHorz();
> 
>  
>  
> println("PITCH: "+pitch," ROLL: "+roll,"YAW: "+yaw);
> //println("Linear compass : DEGREE = "+ degree," YAW DEGREE = "+ (360-(yawDeg%360)));
>   
> }
> 
> / ********************************************************** /
> /* DRAW ROLL/PITCH INDICATOR */
> / ********************************************************** /
> void drawHorz() {
>   
> ahm.beginDraw();
> ahm.translate(0,0);  
>   
> // int pitchDeg = 360-(pitch%360);
> //Bank = (int)map(roll, 0, 360, 0, 360);
> //rollDeg = (int)((Bank%360));
> 
> if((int)Pitch >= 1 && (int)Pitch <= 180)
> pitchDeg = (int)pitch;
> else pitchDeg = 360 + (int)Pitch;
>  
> //Pitch = (int)map(pitch, 0, 360, 0, 360);
> //pitchDeg = (int)((Pitch%360));
> pitchDeg = ((int) map(pitch, 00, width, -95, 95));
> pitchDeg = constrain(pitch%360, -90, 90);
>   
> int ym = 160-90; // y margin
> pitchDeg = (int) map(pitch, -90, 90, ym, 300- ym); // You want this mapped to the scale
> pitchDeg = (pitchDeg%360 + 360) % 360; // keep pitchDeg positive
>  
> println("pitch: ",+pitch," Pitch:",+Pitch," pitchDeg: ",(pitchDeg%360));
>   
> ahm.noStroke();
>   
> ahm.fill(0, 180, 255);//fills sky
> ahm.rect(0, 0, 300, 300);
>   
> ahm.fill(95, 55, 40); //Fills ground
> ahm.rect(0, ((pitchDeg%360)), 300, pitch); 
>   
> ahm.translate(150,150); // Places pitch scale center
> ahm.rotate(-PI-PI/6); // Centers upper scale
>  
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;  
>  
> CircularScale2(); //Draws upper scale
> ahm.rotate(PI+PI/6); // Centers lower scale
> ahm.rotate(-PI/6); //
> CircularScale2(); //Draws lower scale
> ahm.rotate(PI/6); // Aligns pitch scale and axis
> //ahm.rotate(-rollDeg%360); 
>   
>   
> PitchScale();
> Axis(); 
> //ahm.pop();
> //ahm.rotate(rollDeg%360);
> ahm.endDraw();
> image(ahm, 280, 390);
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale2()
> {
> float GaugeWidth=304;  
> ahm.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> ahm.strokeWeight(2*StrokeWidth);
> ahm.stroke(255);
> ahm.noFill();
>    
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));  
>      
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;  
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> ahm.strokeWeight(5);
> ahm.stroke(255);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> ahm.strokeWeight(1);
> ahm.stroke(100, 255, 100);
>       
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> else
> {
> ahm.strokeWeight(3);
> ahm.stroke(255);
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> / *************************************************** /
> /* Draw the pitch scale */
> / *************************************************** /
> void PitchScale()
> { 
>   
> //ahm.push();
> //ahm.translate(0,0);
>   
> ahm.stroke(255);
> ahm.fill(255);
> ahm.strokeWeight(2);
> ahm.textSize(14);
> ahm.push();
> ahm.translate(0,0);
> ahm.textAlign(CENTER,CENTER);
> for (int i=-4;i<5;i++)
> {  
> if ((i==0)==false)
> {
> ahm.line(40, 20*i, -40, 20*i); // Draws wider lines
> }  
>         
> ahm.text(""+i*20, 20, 20*i -10, -150, 20); // draws left side degrees   
> ahm.text(""+i*20, -20, 20*i -10, 150, 20); // draws right side degrees
>    
> }
> //textAlign(CORNER);
> ahm.strokeWeight(1);
> for (int i=-9;i<10;i++)
> {  
> if ((i==0)==false)
> {    
> ahm.line(20, 10*i, -20, 10*i); // Draws narrower lines
> }
> }
> ahm.pop();
> }
> 
> void serialEvent (Serial fd) 
> {
> // get the ASCII string:
> String rpstr = fd.readStringUntil('\n');
> if (rpstr != null) {
> String[] list = split(rpstr, ':');
> pitch = ((int)float(list[0]));
> roll = ((int)float(list[1]));
> yaw = ((int)float(list[2]));
>         
> }
> }
> 
> ```

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [March 29, 2026, 11:04pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/31 "2026-03-29T23:04:46Z")

</div>

Hello @philtkp,

I played with your code a bit.

Open at your own risk:

> **Summary**
>
> ```processing
> // LINEAR COMPASS
> import processing.serial.*;
> Serial fd;
> 
> PGraphics hdg;
> PGraphics comp;
> PGraphics ahm;
> PImage fade;
> 
> float Pitch;
> float Bank;
> float Azimuth;
> float degree1;
> //float yaw = 0.0f;
> float yawOffset = 0.0f;
> float SpanAngle=120;
> //float GaugeWidth=304;
> //float StrokeWidth=1;
> //float an;
> //float DivxPhasorCloser;
> //float DivxPhasorDistal;
> //float DivyPhasorCloser;
> //float DivyPhasorDistal;
> int W=300; //My Laptop's screen width
> int H=300;
> int degree = 0;
> int heading = 0;
> int pitch = 0;
> int roll = 0;
> int yaw = 0;
> int yawDeg = 0;
> int rollDeg = 0;
> int pitchDeg = 0;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions;
> PVector v1, v2;
> 
> int m = 2; // Multiplier
> 
> //
> // ---------------------------------------------------------------
> //
> 
> void setup()
> {
> // init
> size(400, 400, P3D);
> 
> ahm = createGraphics(300, 300);
> 
> //fd = new Serial(this, Serial.list()[1], 115200);
> //// Defer callback until new line
> //fd.bufferUntil('\n');
> 
> rectMode(CENTER);
> smooth();
> strokeCap(SQUARE);//Optional
> background(0);
> } // func
> 
> void draw()
> { 
> background(128);
>    
> translate(width/2, height/2);
> imageMode(CENTER);
>   
> m = 1; // 1 for 90, 2 for 180 scale Just the scale!
>   
> int range = 100;
>   
> // Only focused on this now:
> pitch = (int) map(mouseY, 0, height, -range, range); //global
>   
> // Only focused on this now:
> roll = (int) map(mouseX, 0, height, -range, range); //global
> 
> if ((abs(roll) | abs(pitch)) > 90) // DAnger! Danger!
> background(255, 0, 0);
> 
> int ym = 150-90; // y margin
> pitchDeg = (int) map(pitch, -90, 90, ym, 300-ym); // You want this mapped to the scale  
> drawHorz(pitchDeg);
>   
> push();
> //rotateZ(radians(roll));
> image(ahm, 0, 0);
> pop();
> 
> println("PITCH: "+pitch, " ROLL: "+roll, "YAW: "+yaw);
> }
> 
> / ********************************************************** /
> /* DRAW ROLL/PITCH INDICATOR */
> / ********************************************************** /
> void drawHorz(int pitchDeg) {
> 
> ahm.beginDraw();
> ahm.translate(0, 0);
> ahm.noStroke();
> 
> ahm.fill(0, 180, 255);//fills sky
> ahm.rect(0, 0, 500, 500);
> 
> ahm.fill(95, 55, 40); //Fills ground
> //ahm.rect(0, 0, 300, pitchDeg);
>   
> // First atempt:
> //ahm.beginShape();
> //ahm.vertex(0, 0);
> //ahm.vertex(300, 0);
> //ahm.vertex(300, pitchDeg + 150*sin(radians(roll)));
> //ahm.vertex(0, pitchDeg - 150*sin(radians(roll)));  
> //ahm.endShape();
>   
> float halfW = 300/2; // rectangle half-width
> float rollShift = tan(radians(roll)) * halfW;  
>   
> ahm.beginShape();
> ahm.vertex(0, pitchDeg - rollShift); // top-left moves up/down
> ahm.vertex(300, pitchDeg + rollShift); // top-right moves up/down
> ahm.vertex(300, 300); // bottom-right stays
> ahm.vertex(0, 300); // bottom-left stays
> ahm.endShape(CLOSE);
>     
> ahm.translate(150, 150); // Places pitch scale center
> ahm.rotate(-PI-PI/6); // Centers upper scale
> 
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;
> 
> CircularScale2(); //Draws upper scale
> ahm.rotate(PI+PI/6); // Centers lower scale
> ahm.rotate(-PI/6); //
> CircularScale2(); //Draws lower scale
> ahm.rotate(PI/6); // Aligns pitch scale and axis
>   
> ahm.rotate(radians(roll));
> 
> PitchScale();
> ahm.endDraw();
> }
> / ******************************************************** /
> /* Draw cirular scale */
> /* Code from Adrian Fernandez 4-19-2013 */
> / ******************************************************** /
> void CircularScale2()
> {
> float GaugeWidth=304;
> ahm.textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> ahm.strokeWeight(2*StrokeWidth);
> ahm.stroke(255);
> ahm.noFill();
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0; Division<NumberOfScaleMinorDivisions+1; Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> 
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0; Division<NumberOfScaleMajorDivisions+1; Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> ahm.strokeWeight(5);
> ahm.stroke(255);
> 
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> ahm.strokeWeight(1);
> ahm.stroke(100, 255, 100);
> 
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> } else
> {
> ahm.strokeWeight(3);
> ahm.stroke(255);
> ahm.line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> / *************************************************** /
> /* Draw the pitch scale */
> / *************************************************** /
> void PitchScale()
> {
> ahm.stroke(255);
> ahm.fill(255);
> ahm.strokeWeight(2);
> ahm.textSize(14);
> ahm.push();
> ahm.translate(0, 0);
> ahm.textAlign(CENTER, CENTER);
> for (int i=-4; i<5; i++)
> {
> if ((i==0)==false)
> {
> ahm.line(40, 20*i, -40, 20*i); // Draws wider lines
> }
> 
> ahm.text(""+i*20*m, 20, 20*i -10, -150, 20); // draws left side degrees
> ahm.text(""+i*20*m, -20, 20*i -10, 150, 20); // draws right side degrees
> }
> //textAlign(CORNER);
> ahm.strokeWeight(1);
> for (int i=-9; i<10; i++)
> {
> if ((i==0)==false)
> {
> ahm.line(20, 10*i, -20, 10*i); // Draws narrower lines
> }
> }
> ahm.pop();
> }
> 
> ```

Roll and pitch are typically +/- 90 for Euler angles.  
You will run into issues greater than this and may have to give it further consideration as the axis flip and you get Gimbal lock.  
You may have to consider quaternions… I had these issues before and manage to sort it out. That was a while back!

Try and provide runnable code please.  
Mapping for mouse to represent your ranges helps.  
Some of the naming was hard to read like _Pitch_ and _pitch_; variable names should not be upper case (Java rules) and these are too similar.  
It is easy to rename everything with the Processing _rename_ utility!

I hope the feedback is well received as it was intended to be.

Have fun!

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [March 30, 2026, 12:04am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/32 "2026-03-30T00:04:57Z")

</div>

Risk has been Taken. I’m still here. Your feedback has been well received. This is all a learning curve for me. I’ll have to read up on quaternions. I’ve been trying to clean up my code. I see what you mean by some of the naming variables. I’m glad that is is the only thing that keeps me up at night these days.

I stepped back a bit to make sure i was understanding the use of translate(). Now i have a question. since i’ve been using PGraphics to display my screens without a problem. till now, i’m unable to display a cube or a shape using PGraphics. acn these be used together? I was able to get what wanted to do but just curious. this way im to visualize my bearings

 ![pointer](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/d/8/d8849f7dc3f6584848206da949475e6bf1e54dce.jpeg)

I really appreciate the knowledge you have shared. 🍺

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [April 1, 2026, 3:20pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/34 "2026-04-01T15:20:48Z")

</div>

> [@philtkp](#):
>
> i’m unable to display a cube or a shape using PGraphics

Exploring _PGraphics_ and got a bit carried away with this one!  
There may be something in there that can help.

It is April 1… so open at your own risk!

> **Code**
>
> April Fools! This is not April Fools code. `:)`
> 
> _[April Fools' Day - Wikipedia](https://en.wikipedia.org/wiki/April_Fools%27_Day)_
> 
> See Gallery post:  
> [Lost in Space PGraphics Exploration](https://discourse.processing.org/t/lost-in-space-pgraphics-exploration/48391)

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [April 1, 2026, 4:32pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/35 "2026-04-01T16:32:00Z")

</div>

Nice! 😄

I just stepped back a bit to see that i understood the commands i was using. it has helped a lot.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/2/7/27def5ca3e34bbc63babbb6314a6c7d335d2d8eb.png)

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [April 3, 2026, 4:04am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/36 "2026-04-03T04:04:28Z")

</div>

@glv

I’ve been trying several without any progress. I was wondering if this could be done with a sphere. wrapping it with a jpg of the horizon.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/7/e/7ebeac7ef2c4956c001ad9d0643716cad466fff7.png)

now i just need to figure out how to control the pitch and the roll properly. You think this is a good alternative? I tried it with the code i have, but it’s a bit quirky

> **CODE**
>
> This is some code i found on the forum from 2017
> 
> ```auto
> void setup() { 
> size(600, 600, P3D); 
> background(0); 
>   
> PImage earth = loadImage( "horz.jpg");
> globe = createShape(SPHERE, 100); 
> globe.setStroke(false);
> globe.setTexture(earth);
> noStroke();
> }
> 
> void draw() {
> background(0);
> translate(width/2, height/2);
> rotateY(map(mouseX,0,width,-PI,PI));
> rotateZ(map(mouseY,0,width,-PI,PI));
> shape(globe);
> }
> 
> ```

 ![horz](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/9/b/9bf2cb6eb0fcfca35a39621496c8f409ddcdd7a1.jpeg)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [April 3, 2026, 4:03pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/37 "2026-04-03T16:03:43Z")

</div>

> [@philtkp](#):
>
> now i just need to figure out how to control the pitch and the roll properly. You think this is a good alternative?

You will figure it out!  
The order matters for Euler rotations… yaw, pitch and roll.

I suggest writing some keyboard controls for the simulations.  
I did not include the “extras” in my code below.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/6/b/6bb4ea79d3f6c85eb96e465792338f6b68e2f4f0.png)

I remember having one of those floating sphere compasses in my Ford Falcon way back when!

> **Code**
>
> ```processing
> float yaw, pitch, roll;
> float displayYaw; 
> float offset;
> int timer = 0;
> 
> //PGraphics pg; 
> 
> PShape globe;
> 
> public void settings()
> { 
> size(500, 500, P3D);
> smooth();
> }
> 
> public void setup()
> {
> /* size commented out by preprocessor */;
>   
> surface.setLocation(10, 10); // Fixes occasional issue of not displaying!
> surface.setTitle("Lost in Space");
>   
> background(0); //Initial
>   
> // Imeage was generated with this:
> //pg = createGraphics(4*360, 4*180, P2D);
> //createHorizon();
>   
> // Initial styles  
> textSize(24);  
> /* smooth commented out by preprocessor */; // This is the default
>     
> PImage earth = loadImage("horizon.png");
> globe = createShape(SPHERE, 100); 
> globe.setStroke(false);
> //globe.setTexture(pg);
> globe.setTexture(earth);
> } 
> 
> public void draw()
> {
> background(0);
>   
> //Common styles (consider moving to setup() )
> fill(255);
> textSize(24); //Always set maximum size in setup()
> textAlign(CENTER, CENTER);
>   
> push(); 
> translate(width/2, height/2);
> lights();
>  
> //ypr(); // keyboard and\or mouse controls not included
>   
> if (frameCount<2*60)
> {
> offset = 0;
> yaw = pitch = roll = 0;
> println(frameCount);
> }
> else if( frameCount>=2*60 && frameCount<4*60) 
> {
> offset = -90 - 30; // Try with offset set to 0 to see why I did this  
> yaw = pitch = roll = 0;
> println(frameCount);    
> }  
> else
> {
> // These are in degrees
> offset = -90 - 30; // Try with offset set to 0 to see why I did this  
> yaw = timer/3.0;
> pitch = 10*sin(timer*(TAU/(4*360)));
> roll = 10*sin(timer*(TAU/(0.5*360)));
>     
> timer++;
> }
>   
> angleEulerUpdate(yaw, pitch, roll, offset);
>   
> shape(globe);
> pop(); 
> }
> 
> public void angleEulerUpdate(float yaw, float pitch, float roll, float offset)
> {       
> rotateY(radians(offset)); // This is for model alignment. 
> rotateY(radians(-yaw)); // later
> rotateZ(radians(pitch)); // pitch
> rotateX(radians(roll)); // roll  
> }
> 
> ```

I generated an image with a _PGraphics_ to provide some sense of orientation and understand the texturing and saved it as an image:

 ![horizon](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/6/a/6ace06558f4afb6872e001e4329a17b57ee2330e.png)

The 0 (1/2 of it) was lost in initial attempts so I offset it and then adjusted in the code.

Have fun! I know I am.

Reference:

_[PShape / Processing.org](https://processing.org/tutorials/pshape/#3d-and-textures)_ \< 3D textures

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [April 13, 2026, 6:39pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/38 "2026-04-13T18:39:10Z")

</div>

```processing

import processing.serial.*;
Serial fd;

PShape globe;

float SpanAngle=120;
float yawOffset = 0.0f;

int pitch = 0;
int roll = 0;
int yaw = 0;
int NumberOfScaleMajorDivisions;
int NumberOfScaleMinorDivisions;

void setup() {
size(600, 600, P3D);

// fd = new Serial(this, Serial.list()[1], 115200);
// fd.bufferUntil(‘\n’);

// background(0);
//String http = “http://”;
PImage earth = loadImage( “horz.jpg”);
globe = createShape(SPHERE, 170);
globe.setStroke(false);
globe.setTexture(earth);
noStroke();
}

void draw() {
background(0);

translate(width/2,height/2); // Places pitch scale center

push();
translate(0,0,170); // places pitch scaale and axis infront of globe

//rotateZ(radians(roll));
rotateZ(map(mouseX,0,width,-PI,PI));
Axis();
PitchScale();

rotate(-PI-PI/6); // Centers upper scale

SpanAngle=120;
NumberOfScaleMajorDivisions=12;
NumberOfScaleMinorDivisions=24;  

CircularScale(); //Draws upper scale
rotate(PI+PI/6); // Centers lower scale
rotate(-PI/6); //
CircularScale(); //Draws lower scale
rotate(PI/6); // Aligns pitch scale and axis

pop();

rotateX(map(mouseY,0,width,-PI,PI));
//rotateX(radians(pitch));
//rotateZ(radians(roll));

shape(globe);

}

///
// Draw red axis lines /
///
void Axis()
{
stroke(255,0,0);
strokeWeight(2);
line(-45, 0, 45, 0);
line(0, 90, 0, -90);
fill(100, 255, 100); //GREEN
stroke(0);
strokeWeight(1);
triangle(0, -107, -6, -90, 6, -90);
triangle(0, 107, -6, 90, 6, 90);
}

///
/ Draw the pitch scale /
///
void PitchScale()
{

//ahm.push();
//ahm.translate(0,0);

stroke(255);
fill(255);
strokeWeight(2);
textSize(14);
// push();
translate(0,0);
textAlign(CENTER,CENTER);
for (int i=-4;i<5;i++)
{
if ((i==0)==false)
{
line(40, 20i, -40, 20i); // Draws wider lines
}

text(“”+i20, 20, 20i -10, -150, 20); // draws left side degrees
text(“”+i20, -20, 20i -10, 150, 20); // draws right side degrees

}
//textAlign(CORNER);
strokeWeight(1);
for (int i=-9;i<10;i++)
{
if ((i==0)==false)
{
line(20, 10i, -20, 10i); // Draws narrower lines
}
}

//image(ahmglass,-148,-148);
}
///
//* Draw cirular scale /
// Code from Adrian Fernandez 4-19-2013 */
///
void CircularScale()
{
float GaugeWidth=304;
textSize(GaugeWidth/20);
float StrokeWidth=1;
float an;
float DivxPhasorCloser;
float DivxPhasorDistal;
float DivyPhasorCloser;
float DivyPhasorDistal;
strokeWeight(2*StrokeWidth);
stroke(255);
noFill();

float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;

for (int Division=0;Division<NumberOfScaleMinorDivisions+1;Division++)
{
an=SpanAngle/2+DivisionSpanAngle/NumberOfScaleMinorDivisions;
DivxPhasorCloser=DivCloserPhasorLenghtcos(radians(an));
DivxPhasorDistal=DivDistalPhasorLenghtcos(radians(an));
DivyPhasorCloser=DivCloserPhasorLenghtsin(radians(an));
DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));

line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);

}

DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;

for (int Division=0;Division<NumberOfScaleMajorDivisions+1;Division++)
{
an=SpanAngle/2+DivisionSpanAngle/NumberOfScaleMajorDivisions;
DivxPhasorCloser=DivCloserPhasorLenghtcos(radians(an));
DivxPhasorDistal=DivDistalPhasorLenghtcos(radians(an));
DivyPhasorCloser=DivCloserPhasorLenghtsin(radians(an));
DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
{
strokeWeight(5);
stroke(255);

  line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
  strokeWeight(1);
  stroke(100, 255, 100);
  
  line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
}
else
{
  strokeWeight(3);
  stroke(255);
  line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
}

}
}

// ***************************************************
//*
// ***************************************************
void serialEvent (Serial fd)
{
// get the ASCII string:
String rpstr = fd.readStringUntil(‘\n’);
if (rpstr != null) {
String
 list = split(rpstr, ‘:’);
pitch = ((int)float(list[0]));
roll = ((int)float(list[1]));
yaw = ((int)float(list[2]));

}
}

```

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [April 13, 2026, 6:57pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/39 "2026-04-13T18:57:43Z")

</div>

Sometimes it is one of these when rotating text (from my experience):

> **[hint() / Reference](https://processing.org/reference/hint_.html)**
>
> This function is used to enable or disable special features that control how graphics are drawn. In the course of developing Processing, we had to make hard decisions about tradeoffs between performan…

In your case I moved the shape up:

```processing
  translate(width/2, height/2, -100); // Places pitch scale center
  
  push();
  rotateX(map(mouseY,0,width,-PI,PI));
  //rotateX(radians(pitch));
  //rotateZ(radians(roll));
   
  shape(globe);
  pop();

```

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [April 14, 2026, 1:37am UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/40 "2026-04-14T01:37:14Z")

</div>

glv

> **CODE**
>
> ```processing
> 
> import processing.serial.*;
> Serial fd;
> 
> PShape globe;
> 
> float SpanAngle=120;
> float yawOffset = 0.0f;
> 
> int pitch = 0;
> int roll = 0;
> int yaw = 0;
> int NumberOfScaleMajorDivisions;
> int NumberOfScaleMinorDivisions;
> 
> void setup() {
> 
> size(600, 600, P3D);
> 
> // fd = new Serial(this, Serial.list()[1], 115200);
> //fd.bufferUntil('\n');
> 
> PImage earth = loadImage( "horz.jpg");
> globe = createShape(SPHERE, 170);
> globe.setStroke(false);
> globe.setTexture(earth);
> noStroke();
> }
> 
> void draw() {
> 
> background(0);
> translate(width/2, height/2, -100); // Places pitch scale center
> push();
> rotateX(map(mouseY,0,width,-PI,PI));
> rotateZ(map(mouseX,0,width,-PI,PI)); // <---------------------
> //rotateX(radians(pitch));
> //rotateZ(radians(roll));
> 
> shape(globe);
> 
> pop();
> 
> push();
> translate(0, 0, 170); // places pitch scaale and axis infront of globe
> 
> push();
> rotateZ(map(mouseX,0,width,-PI,PI));// <-----------------------
> //rotateZ(radians(roll));
>   
> PitchScale();
> Axis();
> pop();
> 
> rotate(-PI-PI/6); // Centers upper scale
> 
> SpanAngle=120;
> NumberOfScaleMajorDivisions=12;
> NumberOfScaleMinorDivisions=24;
> 
> CircularScale(); //Draws upper scale
> rotate(PI+PI/6); // Centers lower scale
> rotate(-PI/6); //
> CircularScale(); //Draws lower scale
> rotate(PI/6); // Aligns pitch scale and axis
> pop();
> }
> 
> // *************************************************** /
> //* Draw red axis lines */
> // *************************************************** /
> void Axis()
> {
> stroke(255, 0, 0);
> strokeWeight(2);
> line(-45, 0, 45, 0);
> line(0, 90, 0, -90);
> fill(100, 255, 100); //GREEN
> stroke(0);
> strokeWeight(1);
> triangle(0, -107, -6, -90, 6, -90);
> triangle(0, 107, -6, 90, 6, 90);
> }
> 
> // *************************************************** /
> /* Draw the pitch scale */
> // *************************************************** /
> void PitchScale()
> {
> 
> stroke(255);
> fill(255);
> strokeWeight(2);
> textSize(14);
> // push();
> translate(0, 0);
> textAlign(CENTER, CENTER);
> for (int i=-4; i<5; i++)
> {
> if ((i==0)==false)
> {
> line(40, 20*i, -40, 20*i); // Draws wider lines
> }
> 
> text(""+i*20, 20, 20*i -10, -150, 20); // draws left side degrees
> text(""+i*20, -20, 20*i -10, 150, 20); // draws right side degrees
> }
> //textAlign(CORNER);
> strokeWeight(1);
> for (int i=-9; i<10; i++)
> {
> if ((i==0)==false)
> {
> line(20, 10*i, -20, 10*i); // Draws narrower lines
> }
> }
> 
>   
> }
> // ******************************************************** /
> //* Draw circular scale */
> //* Code from Adrian Fernandez 4-19-2013 */
> // ******************************************************** /
> void CircularScale()
> {
> float GaugeWidth=304;
> textSize(GaugeWidth/20);
> float StrokeWidth=1;
> float an;
> float DivxPhasorCloser;
> float DivxPhasorDistal;
> float DivyPhasorCloser;
> float DivyPhasorDistal;
> strokeWeight(2*StrokeWidth);
> stroke(255);
> noFill();
> 
> float DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/9-StrokeWidth;
> float DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.5-StrokeWidth;
> 
> for (int Division=0; Division<NumberOfScaleMinorDivisions+1; Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMinorDivisions;
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> 
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> 
> DivCloserPhasorLenght=GaugeWidth/2-GaugeWidth/10-StrokeWidth;
> DivDistalPhasorLenght=GaugeWidth/2-GaugeWidth/7.4-StrokeWidth;
> 
> for (int Division=0; Division<NumberOfScaleMajorDivisions+1; Division++)
> {
> an=SpanAngle/2+Division*SpanAngle/NumberOfScaleMajorDivisions;
> DivxPhasorCloser=DivCloserPhasorLenght*cos(radians(an));
> DivxPhasorDistal=DivDistalPhasorLenght*cos(radians(an));
> DivyPhasorCloser=DivCloserPhasorLenght*sin(radians(an));
> DivyPhasorDistal=DivDistalPhasorLenght*sin(radians(an));
> if (Division==NumberOfScaleMajorDivisions/2|Division==0|Division==NumberOfScaleMajorDivisions)
> {
> strokeWeight(5);
> stroke(255);
> 
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> strokeWeight(1);
> stroke(100, 255, 100);
> 
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> } else
> {
> strokeWeight(3);
> stroke(255);
> line(DivxPhasorCloser, DivyPhasorCloser, DivxPhasorDistal, DivyPhasorDistal);
> }
> }
> }
> 
> // ***************************************************
> //*
> // ***************************************************
> void serialEvent (Serial fd)
> {
> // get the ASCII string:
> String rpstr = fd.readStringUntil('\n');
> if (rpstr != null) {
> String[] list = split(rpstr, ':');
> pitch = ((int)float(list[0]));
> roll = ((int)float(list[1]));
> yaw = ((int)float(list[2]));
> }
> }
> 
> ```

I have it working like I think it should (needs to be cleaned up) the only thing I’m having an issue with is, I had to use the same call twice (rotateZ(map(mouseX,0,width,-PI,PI))😉 to make the globe, axis and pitch scale rotate together without also rotating the Circular Scale. I’ll keep at it.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/a/2/a2e7e0fcf7b22696abdfc31e90a81551a8d16fe9.png)

---

<div class="post-metadata">

**Author:** ![glv](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/glv/32/18785_2.png) [@glv](https://discourse.processing.org/u/glv)\
**Post date:** [April 15, 2026, 8:53pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/41 "2026-04-15T20:53:03Z")

</div>

Hello @philtkp,

You may consider something like this:

```auto
void draw()
  {
  background(0);
  lights();

  float rollAngle = map(mouseX, 0, width, -PI, PI);
  float pitchAngle = map(mouseY, 0, height, -PI, PI);

  translate(width/2, height/2, zOffset);

  drawGlobe(pitchAngle, rollAngle);  
  drawOverlay(rollAngle);
  }

```

`:)`

---

<div class="post-metadata">

**Author:** ![philtkp](https://avatars.discourse-cdn.com/v4/letter/p/6a8cbe/32.png) [@philtkp](https://discourse.processing.org/u/philtkp)\
**Post date:** [April 18, 2026, 6:52pm UTC](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060/42 "2026-04-18T18:52:37Z")

</div>

@glv

Thought i would give the square version of the indicator another try.

> **CODE**
>
> ```processing
> int pitch;
> int yaw;
> int roll;
> 
> void setup()
> {
> size(400,400,P3D);
> smooth();
> }
> 
> void draw()
> {
> 
> background(0); 
>   
> pitch = ((int) map(mouseY-200,0, 400, 0, 1200));
> roll = ((int) map(mouseX,0, 360, 0, 360));
>   
> translate(width/2,height/2); 
> stroke(0);
>   
>   
>  
> println("PITCH: ",pitch + " ROLL: ", roll);
>   
> pushMatrix();
> 
> rotate(radians(roll%360));
>  
> noStroke();
>   
> fill(0, 180, 255); // SKY COLOR
> rect(-250, -250, width+100, height+100 );
>   
> fill(95, 55, 40); // GROUND COLOR
> rect(-250, pitch-250, width+100, height+100);
>   
> stroke(255);
> strokeWeight(1);
> line(-250, pitch-250, 250, pitch-250); // Draw line to see center of horizon separation
>   
> popMatrix();
>   
> stroke(255);
> strokeWeight(1);
> line(0, -100, 0, 100); // Draw cross hairs for visual reference
> line(-100, 0, 100, 0);
>   
> stroke(0);
> strokeWeight(60);
> noFill();
> rect(-200, -200, width, height,75); // Draw the outside frame
>   
> }
> 
> ```

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/processingfoundation1/original/3X/e/2/e267df14fe00a7257115e1c8fa517efc26cd747b.png)

[Previous page](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060.md?page=1)

[Next page](https://discourse.processing.org/t/linear-compass-display-to-use-with-a-mpu/48060.md?page=3)
