diff --git a/src.csharp/AlphaTab/Core/TypeHelper.cs b/src.csharp/AlphaTab/Core/TypeHelper.cs
index bee284939..5ff36f5c7 100644
--- a/src.csharp/AlphaTab/Core/TypeHelper.cs
+++ b/src.csharp/AlphaTab/Core/TypeHelper.cs
@@ -3,6 +3,7 @@
 using System.Globalization;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Text;
 using System.Threading.Tasks;
 using AlphaTab.Core.EcmaScript;
 
@@ -487,6 +488,23 @@ public static string[] Split(this string value, RegExp pattern)
             return pattern.Split(value);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string Repeat(this string value, double count)
+        {
+            var icount = (int)count;
+            if (icount == 0)
+            {
+                return "";
+            }
+
+            var builder = new StringBuilder(value.Length * icount);
+            for (var i = 0; i < icount; i++)
+            {
+                builder.Append(value);
+            }
+            return builder.ToString();
+        }
+
         public static Task CreatePromise(Action<Action, Action<object>> run)
         {
             var taskCompletionSource = new TaskCompletionSource<object?>();
diff --git a/src.kotlin/alphaTab/android/src/main/java/alphaTab/core/Globals.kt b/src.kotlin/alphaTab/android/src/main/java/alphaTab/core/Globals.kt
index 154f8045c..652a9a83a 100644
--- a/src.kotlin/alphaTab/android/src/main/java/alphaTab/core/Globals.kt
+++ b/src.kotlin/alphaTab/android/src/main/java/alphaTab/core/Globals.kt
@@ -276,3 +276,8 @@ internal fun <T> Deferred<T>.catch(callback: (alphaTab.core.ecmaScript.Error) ->
 @OptIn(ExperimentalUnsignedTypes::class)
 internal val ArrayBuffer.byteLength: Int
     get() = this.size
+
+internal fun String.repeat(count:Double): String {
+    return this.repeat(count.toInt())
+}
+
diff --git a/src/Environment.ts b/src/Environment.ts
index 01aac4d81..e5ecff464 100644
--- a/src/Environment.ts
+++ b/src/Environment.ts
@@ -68,6 +68,7 @@ import { SustainPedalEffectInfo } from './rendering/effects/SustainPedalEffectIn
 import { GolpeEffectInfo } from './rendering/effects/GolpeEffectInfo';
 import { GolpeType } from './model/GolpeType';
 import { WahPedalEffectInfo } from './rendering/effects/WahPedalEffectInfo';
+import { BeatBarreEffectInfo } from './rendering/effects/BeatBarreEffectInfo';
 
 export class LayoutEngineFactory {
     public readonly vertical: boolean;
@@ -509,7 +510,8 @@ export class Environment {
             // Score (standard notation)
             new EffectBarRendererFactory(Environment.StaffIdBeforeScoreAlways, [
                 new FermataEffectInfo(),
-                new WahPedalEffectInfo()
+                new BeatBarreEffectInfo(),
+                new WahPedalEffectInfo(),
             ]),
             new EffectBarRendererFactory(
                 Environment.StaffIdBeforeScoreHideable,
diff --git a/src/NotationSettings.ts b/src/NotationSettings.ts
index a9dfddbb1..9386672ae 100644
--- a/src/NotationSettings.ts
+++ b/src/NotationSettings.ts
@@ -311,7 +311,12 @@ export enum NotationElement {
     /**
      * The Wah effect signs above and below the staff.
      */
-    EffectWahPedal
+    EffectWahPedal,
+
+    /**
+     * The Beat barre effect signs above and below the staff "1/2B IV ─────┐"
+     */
+    BeatBarre
 }
 
 /**
diff --git a/src/exporter/GpifWriter.ts b/src/exporter/GpifWriter.ts
index 5338bd127..2cee50d8d 100644
--- a/src/exporter/GpifWriter.ts
+++ b/src/exporter/GpifWriter.ts
@@ -3,6 +3,7 @@ import { MidiUtils } from '@src/midi/MidiUtils';
 import { AccentuationType } from '@src/model/AccentuationType';
 import { AutomationType } from '@src/model/Automation';
 import { Bar, SustainPedalMarkerType } from '@src/model/Bar';
+import { BarreShape } from '@src/model/BarreShape';
 import { Beat } from '@src/model/Beat';
 import { BendPoint } from '@src/model/BendPoint';
 import { BrushType } from '@src/model/BrushType';
@@ -819,6 +820,18 @@ export class GpifWriter {
                 this.writeSimplePropertyNode(beatProperties, 'VibratoWTremBar', 'Strength', 'Slight');
                 break;
         }
+
+        if (beat.isBarre) {
+            this.writeSimplePropertyNode(beatProperties, 'BarreFret', 'Fret', beat.barreFret.toString());
+            switch (beat.barreShape) {
+                case BarreShape.Full:
+                    this.writeSimplePropertyNode(beatProperties, 'BarreString', 'String', '0');
+                    break;
+                case BarreShape.Half:
+                    this.writeSimplePropertyNode(beatProperties, 'BarreString', 'String', '1');
+                    break;
+            }
+        }
     }
 
     private writeRhythm(parent: XmlNode, beat: Beat, rhythms: XmlNode) {
diff --git a/src/generated/model/BeatCloner.ts b/src/generated/model/BeatCloner.ts
index 2bd5ba934..ec686c3bf 100644
--- a/src/generated/model/BeatCloner.ts
+++ b/src/generated/model/BeatCloner.ts
@@ -65,6 +65,8 @@ export class BeatCloner {
         clone.isEffectSlurOrigin = original.isEffectSlurOrigin;
         clone.beamingMode = original.beamingMode;
         clone.wahPedal = original.wahPedal;
+        clone.barreFret = original.barreFret;
+        clone.barreShape = original.barreShape;
         return clone;
     }
 }
diff --git a/src/generated/model/BeatSerializer.ts b/src/generated/model/BeatSerializer.ts
index 2f3b28f61..46e4afedd 100644
--- a/src/generated/model/BeatSerializer.ts
+++ b/src/generated/model/BeatSerializer.ts
@@ -26,6 +26,7 @@ import { DynamicValue } from "@src/model/DynamicValue";
 import { BeamDirection } from "@src/rendering/utils/BeamDirection";
 import { BeatBeamingMode } from "@src/model/Beat";
 import { WahPedal } from "@src/model/WahPedal";
+import { BarreShape } from "@src/model/BarreShape";
 export class BeatSerializer {
     public static fromJson(obj: Beat, m: unknown): void {
         if (!m) {
@@ -81,6 +82,8 @@ export class BeatSerializer {
         o.set("preferredbeamdirection", obj.preferredBeamDirection as number | null);
         o.set("beamingmode", obj.beamingMode as number);
         o.set("wahpedal", obj.wahPedal as number);
+        o.set("barrefret", obj.barreFret);
+        o.set("barreshape", obj.barreShape as number);
         return o;
     }
     public static setProperty(obj: Beat, property: string, v: unknown): boolean {
@@ -225,6 +228,12 @@ export class BeatSerializer {
             case "wahpedal":
                 obj.wahPedal = JsonHelper.parseEnum<WahPedal>(v, WahPedal)!;
                 return true;
+            case "barrefret":
+                obj.barreFret = v! as number;
+                return true;
+            case "barreshape":
+                obj.barreShape = JsonHelper.parseEnum<BarreShape>(v, BarreShape)!;
+                return true;
         }
         return false;
     }
diff --git a/src/importer/AlphaTexImporter.ts b/src/importer/AlphaTexImporter.ts
index e1b39b3b5..7074e42d1 100644
--- a/src/importer/AlphaTexImporter.ts
+++ b/src/importer/AlphaTexImporter.ts
@@ -42,6 +42,7 @@ import { NoteAccidentalMode } from '@src/model';
 import { GolpeType } from '@src/model/GolpeType';
 import { FadeType } from '@src/model/FadeType';
 import { WahPedal } from '@src/model/WahPedal';
+import { BarreShape } from '@src/model/BarreShape';
 
 /**
  * A list of terminals recognized by the alphaTex-parser
@@ -1639,6 +1640,29 @@ export class AlphaTexImporter extends ScoreImporter {
         } else if (syData === 'wahc') {
             this._sy = this.newSy();
             beat.wahPedal = WahPedal.Closed;
+            return true;
+        } else if (syData === 'barre') {
+            this._sy = this.newSy();
+
+            if (this._sy !== AlphaTexSymbols.Number) {
+                this.error('beat-barre', AlphaTexSymbols.Number, true);
+            }
+            beat.barreFret = this._syData as number;
+            this._sy = this.newSy();
+
+            if (this._sy === AlphaTexSymbols.String) {
+                switch ((this._syData as string).toLowerCase()) {
+                    case 'full':
+                        beat.barreShape = BarreShape.Full;
+                        this._sy = this.newSy();
+                        break;
+                    case 'half':
+                        beat.barreShape = BarreShape.Half;
+                        this._sy = this.newSy();
+                        break;
+                }
+            }
+            
             return true;
         } else {
             // string didn't match any beat effect syntax
diff --git a/src/importer/GpifParser.ts b/src/importer/GpifParser.ts
index b77708af3..90040cd01 100644
--- a/src/importer/GpifParser.ts
+++ b/src/importer/GpifParser.ts
@@ -49,6 +49,7 @@ import { Logger } from '@src/Logger';
 import { GolpeType } from '@src/model/GolpeType';
 import { FadeType } from '@src/model/FadeType';
 import { WahPedal } from '@src/model/WahPedal';
+import { BarreShape } from '@src/model/BarreShape';
 
 /**
  * This structure represents a duration within a gpif
@@ -1826,6 +1827,19 @@ export class GpifParser {
                                     parseFloat(c.findChildElement('Float')!.innerText)
                                 );
                                 break;
+                            case 'BarreFret':
+                                beat.barreFret = parseInt(c.findChildElement('Fret')!.innerText);
+                                break;
+                            case 'BarreString':
+                                switch (c.findChildElement('String')!.innerText) {
+                                    case '0':
+                                        beat.barreShape = BarreShape.Full;
+                                        break;
+                                    case '1':
+                                        beat.barreShape = BarreShape.Half;
+                                        break;
+                                }
+                                break;
                         }
                         break;
                 }
diff --git a/src/model/BarreShape.ts b/src/model/BarreShape.ts
new file mode 100644
index 000000000..db17c0ff6
--- /dev/null
+++ b/src/model/BarreShape.ts
@@ -0,0 +1,19 @@
+/**
+ * Lists all beat barré types.
+ */
+export enum BarreShape {
+    /**
+     * No Barré
+     */
+    None,
+
+    /**
+     * Full Barré (play all strings)
+     */
+    Full,
+
+    /**
+     * 1/2 Barré (play only half the strings)
+     */
+    Half
+}
diff --git a/src/model/Beat.ts b/src/model/Beat.ts
index 714077bb4..f797c14e3 100644
--- a/src/model/Beat.ts
+++ b/src/model/Beat.ts
@@ -25,6 +25,7 @@ import { GraceGroup } from '@src/model/GraceGroup';
 import { GolpeType } from './GolpeType';
 import { FadeType } from './FadeType';
 import { WahPedal } from './WahPedal';
+import { BarreShape } from './BarreShape';
 
 /**
  * Lists the different modes on how beaming for a beat should be done.
@@ -476,7 +477,24 @@ export class Beat {
     /**
      * Whether the wah pedal should be used when playing the beat.
      */
-    public wahPedal:WahPedal = WahPedal.None;
+    public wahPedal: WahPedal = WahPedal.None;
+
+    /**
+     * The fret of a barré being played on this beat.
+     */
+    public barreFret: number = -1;
+
+    /**
+     * The shape how the barre should be played on this beat.
+     */
+    public barreShape: BarreShape = BarreShape.None;
+
+    /**
+     * Gets a value indicating whether the beat should be played as Barré
+     */
+    public get isBarre() {
+        return this.barreShape !== BarreShape.None && this.barreFret >= 0;
+    }
 
     public addWhammyBarPoint(point: BendPoint): void {
         let points = this.whammyBarPoints;
diff --git a/src/rendering/effects/BeatBarreEffectInfo.ts b/src/rendering/effects/BeatBarreEffectInfo.ts
new file mode 100644
index 000000000..4ca31ff29
--- /dev/null
+++ b/src/rendering/effects/BeatBarreEffectInfo.ts
@@ -0,0 +1,81 @@
+import { Beat } from '@src/model/Beat';
+import { BarRendererBase } from '@src/rendering/BarRendererBase';
+import { EffectBarGlyphSizing } from '@src/rendering/EffectBarGlyphSizing';
+import { EffectGlyph } from '@src/rendering/glyphs/EffectGlyph';
+import { LineRangedGlyph } from '@src/rendering/glyphs/LineRangedGlyph';
+import { EffectBarRendererInfo } from '@src/rendering/EffectBarRendererInfo';
+import { Settings } from '@src/Settings';
+import { NotationElement } from '@src/NotationSettings';
+import { BarreShape } from '@src/model/BarreShape';
+
+export class BeatBarreEffectInfo extends EffectBarRendererInfo {
+    public get notationElement(): NotationElement {
+        return NotationElement.EffectLetRing;
+    }
+
+    public get canShareBand(): boolean {
+        return false;
+    }
+
+    public get hideOnMultiTrack(): boolean {
+        return false;
+    }
+
+    public shouldCreateGlyph(settings: Settings, beat: Beat): boolean {
+        return beat.isBarre;
+    }
+
+    public get sizingMode(): EffectBarGlyphSizing {
+        return EffectBarGlyphSizing.GroupedOnBeat;
+    }
+
+    public createNewGlyph(renderer: BarRendererBase, beat: Beat): EffectGlyph {
+        let barre = '';
+        switch (beat.barreShape) {
+            case BarreShape.None:
+            case BarreShape.Full:
+                break;
+            case BarreShape.Half:
+                barre += '1/2';
+                break;
+        }
+
+        barre += 'B ' + BeatBarreEffectInfo.toRoman(beat.barreFret);
+
+        return new LineRangedGlyph(barre, false);
+    }
+
+    private static readonly RomanLetters = new Map<string, number>([
+        // ['M', 1000],
+        // ['CM', 900],
+        // ['D', 500],
+        // ['CD', 400],
+        // ['C', 100],
+        // ['XC', 90],
+        ['L', 50],
+        ['XL', 40],
+        ['X', 10],
+        ['IX', 9],
+        ['V', 5],
+        ['IV', 4],
+        ['I', 1]
+    ]);
+
+    public static toRoman(num: number): string {
+        let str = '';
+
+        if (num > 0) {
+            for (var [romanLetter, romanNumber] of BeatBarreEffectInfo.RomanLetters) {
+                const q = Math.floor(num / romanNumber);
+                num -= q * romanNumber;
+                str += romanLetter.repeat(q);
+            }
+        }
+
+        return str;
+    }
+
+    public canExpand(from: Beat, to: Beat): boolean {
+        return from.barreFret === to.barreFret && from.barreShape === to.barreShape;
+    }
+}
diff --git a/src/rendering/glyphs/LineRangedGlyph.ts b/src/rendering/glyphs/LineRangedGlyph.ts
index 1abcaf6af..e56e0b245 100644
--- a/src/rendering/glyphs/LineRangedGlyph.ts
+++ b/src/rendering/glyphs/LineRangedGlyph.ts
@@ -9,10 +9,12 @@ export class LineRangedGlyph extends GroupedEffectGlyph {
     public static readonly LineTopOffset: number = 5;
     public static readonly LineSize: number = 8;
     private _label: string;
+    private _dashed: boolean;
 
-    public constructor(label: string) {
+    public constructor(label: string, dashed: boolean = true) {
         super(BeatXPosition.OnNotes);
         this._label = label;
+        this._dashed = dashed;
     }
 
     public override doLayout(): void {
@@ -40,17 +42,25 @@ export class LineRangedGlyph extends GroupedEffectGlyph {
         let startX: number = cx + this.x + textWidth / 2 + lineSpacing;
         let lineY: number = cy + this.y + 4 * this.scale;
         let lineSize: number = 8 * this.scale;
-        if (endX > startX) {
-            let lineX: number = startX;
-            while (lineX < endX) {
+        if (this._dashed) {
+            if (endX > startX) {
+                let lineX: number = startX;
+                while (lineX < endX) {
+                    canvas.beginPath();
+                    canvas.moveTo(lineX, lineY | 0);
+                    canvas.lineTo(Math.min(lineX + lineSize, endX), lineY | 0);
+                    lineX += lineSize + lineSpacing;
+                    canvas.stroke();
+                }
                 canvas.beginPath();
-                canvas.moveTo(lineX, lineY | 0);
-                canvas.lineTo(Math.min(lineX + lineSize, endX), lineY | 0);
-                lineX += lineSize + lineSpacing;
+                canvas.moveTo(endX, (lineY - 5 * this.scale) | 0);
+                canvas.lineTo(endX, (lineY + 5 * this.scale) | 0);
                 canvas.stroke();
             }
+        } else {
             canvas.beginPath();
-            canvas.moveTo(endX, (lineY - 5 * this.scale) | 0);
+            canvas.moveTo(startX, lineY | 0);
+            canvas.lineTo(endX, lineY | 0);
             canvas.lineTo(endX, (lineY + 5 * this.scale) | 0);
             canvas.stroke();
         }
diff --git a/test-data/visual-tests/effects-and-annotations/barre.gp b/test-data/visual-tests/effects-and-annotations/barre.gp
new file mode 100644
index 000000000..abe128b1d
Binary files /dev/null and b/test-data/visual-tests/effects-and-annotations/barre.gp differ
diff --git a/test-data/visual-tests/effects-and-annotations/barre.png b/test-data/visual-tests/effects-and-annotations/barre.png
new file mode 100644
index 000000000..57359bbf6
Binary files /dev/null and b/test-data/visual-tests/effects-and-annotations/barre.png differ
diff --git a/test/visualTests/features/EffectsAndAnnotations.test.ts b/test/visualTests/features/EffectsAndAnnotations.test.ts
index f8fdef0eb..c5f0bf830 100644
--- a/test/visualTests/features/EffectsAndAnnotations.test.ts
+++ b/test/visualTests/features/EffectsAndAnnotations.test.ts
@@ -1,7 +1,9 @@
 import { AlphaTexImporter } from '@src/importer/AlphaTexImporter';
 import { ByteBuffer } from '@src/io/ByteBuffer';
+import { BeatBarreEffectInfo } from '@src/rendering/effects/BeatBarreEffectInfo';
 import { Settings } from '@src/Settings';
 import { VisualTestHelper } from '@test/visualTests/VisualTestHelper';
+import { expect } from 'chai';
 
 describe('EffectsAndAnnotationsTests', () => {
     it('markers', async () => {
@@ -191,4 +193,41 @@ describe('EffectsAndAnnotationsTests', () => {
     it('golpe-tab', async () => {
         await VisualTestHelper.runVisualTest('effects-and-annotations/golpe-tab.gp');
     });
+
+    it('roman-numbers', () => {
+        expect(BeatBarreEffectInfo.toRoman(0)).to.equal('');
+        expect(BeatBarreEffectInfo.toRoman(1)).to.equal('I');
+        expect(BeatBarreEffectInfo.toRoman(2)).to.equal('II');
+        expect(BeatBarreEffectInfo.toRoman(3)).to.equal('III');
+        expect(BeatBarreEffectInfo.toRoman(4)).to.equal('IV');
+        expect(BeatBarreEffectInfo.toRoman(5)).to.equal('V');
+        expect(BeatBarreEffectInfo.toRoman(6)).to.equal('VI');
+        expect(BeatBarreEffectInfo.toRoman(7)).to.equal('VII');
+        expect(BeatBarreEffectInfo.toRoman(8)).to.equal('VIII');
+        expect(BeatBarreEffectInfo.toRoman(9)).to.equal('IX');
+        expect(BeatBarreEffectInfo.toRoman(10)).to.equal('X');
+        expect(BeatBarreEffectInfo.toRoman(11)).to.equal('XI');
+        expect(BeatBarreEffectInfo.toRoman(12)).to.equal('XII');
+        expect(BeatBarreEffectInfo.toRoman(13)).to.equal('XIII');
+        expect(BeatBarreEffectInfo.toRoman(14)).to.equal('XIV');
+        expect(BeatBarreEffectInfo.toRoman(15)).to.equal('XV');
+        expect(BeatBarreEffectInfo.toRoman(16)).to.equal('XVI');
+        expect(BeatBarreEffectInfo.toRoman(17)).to.equal('XVII');
+        expect(BeatBarreEffectInfo.toRoman(18)).to.equal('XVIII');
+        expect(BeatBarreEffectInfo.toRoman(19)).to.equal('XIX');
+        expect(BeatBarreEffectInfo.toRoman(20)).to.equal('XX');
+        expect(BeatBarreEffectInfo.toRoman(21)).to.equal('XXI');
+        expect(BeatBarreEffectInfo.toRoman(22)).to.equal('XXII');
+        expect(BeatBarreEffectInfo.toRoman(23)).to.equal('XXIII');
+        expect(BeatBarreEffectInfo.toRoman(24)).to.equal('XXIV');
+        expect(BeatBarreEffectInfo.toRoman(25)).to.equal('XXV');
+        expect(BeatBarreEffectInfo.toRoman(26)).to.equal('XXVI');
+        expect(BeatBarreEffectInfo.toRoman(27)).to.equal('XXVII');
+        expect(BeatBarreEffectInfo.toRoman(28)).to.equal('XXVIII');
+        expect(BeatBarreEffectInfo.toRoman(29)).to.equal('XXIX');
+    });
+
+    it('barre', async () => {
+        await VisualTestHelper.runVisualTest('effects-and-annotations/barre.gp');
+    });
 });