diff --git a/android/gradle.properties b/android/gradle.properties
index 4bb5439f682100f8ef4ba80a557fe4f2f0ab14c2..6bf54a6ed821c19f76d860d4a24e7c85d440b575 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.9
-app.versionCode=9
+app.versionName=0.0.10
+app.versionCode=10
diff --git a/assets/translations/en.json b/assets/translations/en.json
index 8c5b323755d16c435eac1201cf9fbb78e4bb91b2..708e712862d32d3fcfa2e45b575a0298303a1bdc 100644
--- a/assets/translations/en.json
+++ b/assets/translations/en.json
@@ -10,6 +10,8 @@
 
   "timeline_title": "Recent scrobbles ({daysCount} days)",
   "counts_by_day": "Counts by day ({daysCount} days)",
+  "counts_by_hour": "Counts by hour ({daysCount} days)",
+
   "MON": "MON",
   "TUE": "TUE",
   "WED": "WED",
diff --git a/assets/translations/fr.json b/assets/translations/fr.json
index a577ce73c0f161fe8297fc3667d41a4a0a314748..4394698622d36ddf4d04c373aaf341256ab824e9 100644
--- a/assets/translations/fr.json
+++ b/assets/translations/fr.json
@@ -10,6 +10,8 @@
 
   "timeline_title": "Écoutes récentes ({daysCount} jours)",
   "counts_by_day": "Écoutes par jour ({daysCount} jours)",
+  "counts_by_hour": "Écoutes par heure ({daysCount} jours)",
+
   "MON": "LUN",
   "TUE": "MAR",
   "WED": "MER",
diff --git a/fastlane/metadata/android/en-US/changelogs/10.txt b/fastlane/metadata/android/en-US/changelogs/10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..eb0128d035ecdea3ae9c51fa5e3f6fdb75088d00
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/10.txt
@@ -0,0 +1 @@
+Add chart "scrobbles counts per hour".
diff --git a/fastlane/metadata/android/fr-FR/changelogs/10.txt b/fastlane/metadata/android/fr-FR/changelogs/10.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6a86d7e679278b35b8ea0f78027a110f2753de8a
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/10.txt
@@ -0,0 +1 @@
+Ajout du graphique du nombre d'écoutes par heure.
diff --git a/lib/models/counts_by_hour.dart b/lib/models/counts_by_hour.dart
new file mode 100644
index 0000000000000000000000000000000000000000..12857528cec1ac7a6dc8af6141a349b9cba11db8
--- /dev/null
+++ b/lib/models/counts_by_hour.dart
@@ -0,0 +1,38 @@
+import 'dart:convert';
+
+class CountsByHourData {
+  final Map<int, double> data;
+
+  const CountsByHourData({
+    required this.data,
+  });
+
+  factory CountsByHourData.fromJson(Map<String, dynamic> json) {
+    Map<int, double> data = {};
+
+    if (json['counts-by-hour'] != null) {
+      json['counts-by-hour'].keys.forEach((day) {
+        if (int.parse(day) != 24) {
+          data[int.parse(day)] = double.parse(json['counts-by-hour'][day].toString());
+        }
+      });
+    }
+
+    return CountsByHourData(data: data);
+  }
+
+  factory CountsByHourData.createEmpty() {
+    return CountsByHourData.fromJson({});
+  }
+
+  String toString() {
+    Map<String, double> map = {};
+
+    this.data.keys.forEach((day) {
+      double? value = this.data[day];
+      map[day.toString()] = value != null ? value.toDouble() : 0.0;
+    });
+
+    return jsonEncode({'counts-by-hour': map});
+  }
+}
diff --git a/lib/network/scrobbles_api.dart b/lib/network/scrobbles_api.dart
index b30cb39e33cff33dddf148b47c6cbf63605d4983..7b0035916a3f32d3cc0788752620346443c6e720 100644
--- a/lib/network/scrobbles_api.dart
+++ b/lib/network/scrobbles_api.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
 import 'package:http/http.dart' as http;
 
 import '../models/counts_by_day.dart';
+import '../models/counts_by_hour.dart';
 import '../models/statistics.dart';
 import '../models/timeline.dart';
 
@@ -39,4 +40,15 @@ class ScrobblesApi {
       throw Exception('Failed to get data from API.');
     }
   }
+
+  static Future<CountsByHourData> fetchCountsByHour(int daysCount) async {
+    final String url = baseUrl + '/data/' + daysCount.toString() + '/counts-by-hour';
+    final response = await http.get(Uri.parse(url));
+
+    if (response.statusCode == 200) {
+      return CountsByHourData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
+    } else {
+      throw Exception('Failed to get data from API.');
+    }
+  }
 }
diff --git a/lib/ui/screens/main_screen.dart b/lib/ui/screens/main_screen.dart
index 1227308233de0f732dcc3018fca9ec7e6959f504..8ac7cca5d32481b1439bb75d35433530fbdd66ca 100644
--- a/lib/ui/screens/main_screen.dart
+++ b/lib/ui/screens/main_screen.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 
 import '../widgets/header.dart';
 import '../widgets/main_screen/counts_by_day_card.dart';
+import '../widgets/main_screen/counts_by_hour_card.dart';
 import '../widgets/main_screen/statistics_card.dart';
 import '../widgets/main_screen/timeline_card.dart';
 
@@ -22,6 +23,8 @@ class MainScreen extends StatelessWidget {
           const ChartTimelineCard(),
           const SizedBox(height: 8),
           const ChartCountsByDayCard(),
+          const SizedBox(height: 8),
+          const ChartCountsByHourCard(),
           const SizedBox(height: 36),
         ],
       ),
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_card.dart b/lib/ui/widgets/main_screen/counts_by_hour_card.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4c030f7ddb406b5ebd4037b225e171bdeb6c1304
--- /dev/null
+++ b/lib/ui/widgets/main_screen/counts_by_hour_card.dart
@@ -0,0 +1,49 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+
+import '../../../models/counts_by_hour.dart';
+import '../../../network/scrobbles_api.dart';
+import '../../../ui/widgets/error.dart';
+import '../../../ui/widgets/main_screen/counts_by_hour_content.dart';
+
+class ChartCountsByHourCard extends StatelessWidget {
+  const ChartCountsByHourCard({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    final int daysCount = 21;
+    late Future<CountsByHourData> futureCountsByHour =
+        ScrobblesApi.fetchCountsByHour(daysCount);
+
+    return FutureBuilder<CountsByHourData>(
+      future: futureCountsByHour,
+      builder: (context, snapshot) {
+        if (snapshot.hasError) {
+          return ShowErrorWidget(message: '${snapshot.error}');
+        }
+
+        return Card(
+          elevation: 2,
+          shadowColor: Theme.of(context).colorScheme.shadow,
+          color: Theme.of(context).colorScheme.primary,
+          shape: const RoundedRectangleBorder(
+            borderRadius: BorderRadius.all(
+              Radius.circular(12),
+            ),
+          ),
+          child: Padding(
+            padding: const EdgeInsets.all(12.0),
+            child: ChartCountsByHourCardContent(
+              daysCount: daysCount,
+              chartData: snapshot.hasData
+                  ? CountsByHourData.fromJson(jsonDecode(snapshot.data.toString()))
+                  : CountsByHourData.createEmpty(),
+              isLoading: !snapshot.hasData,
+            ),
+          ),
+        );
+      },
+    );
+  }
+}
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_chart.dart b/lib/ui/widgets/main_screen/counts_by_hour_chart.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b8154fcb7ac458718fe507aa076f03121bc4beb5
--- /dev/null
+++ b/lib/ui/widgets/main_screen/counts_by_hour_chart.dart
@@ -0,0 +1,192 @@
+import 'package:flutter/material.dart';
+import 'package:fl_chart/fl_chart.dart';
+
+import '../../../config/app_colors.dart';
+import '../../../models/counts_by_hour.dart';
+import '../../../utils/color_extensions.dart';
+
+class CountsByHourCardContentChart extends StatelessWidget {
+  final CountsByHourData chartData;
+
+  const CountsByHourCardContentChart({super.key, required this.chartData});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      height: 100.0,
+      child: LayoutBuilder(builder: (context, constraints) {
+        final double maxWidth = constraints.maxWidth;
+        final int barsCount = this.chartData.data.keys.length;
+        final double barWidth = 0.7 * (maxWidth / barsCount);
+
+        return BarChart(
+          BarChartData(
+            barGroups: getDataCounts(barWidth),
+            backgroundColor: Theme.of(context).colorScheme.onBackground,
+            borderData: getBorderData(),
+            gridData: getGridData(),
+            titlesData: getTitlesData(),
+            barTouchData: getBarTouchData(),
+            maxY: getNextRoundNumber(getMaxCountsValue(), 5),
+          ),
+        );
+      }),
+    );
+  }
+
+  double getMaxCountsValue() {
+    double maxValue = 0;
+
+    this.chartData.data.keys.forEach((key) {
+      double? counts = this.chartData.data[key];
+      if (counts != null) {
+        if (counts > maxValue) {
+          maxValue = counts;
+        }
+      }
+    });
+
+    return maxValue;
+  }
+
+  double getNextRoundNumber(double number, int scale) {
+    return scale * ((number ~/ scale).toInt() + 1);
+  }
+
+  List<BarChartGroupData> getDataCounts(double barWidth) {
+    List<BarChartGroupData> data = [];
+
+    this.chartData.data.keys.forEach((day) {
+      final double? counts = this.chartData.data[day];
+
+      if (counts != null) {
+        final Color barColor = AppColors.contentColorCyan.darken(30);
+
+        data.add(BarChartGroupData(
+          x: day,
+          barRods: [
+            BarChartRodData(
+              toY: counts,
+              color: barColor,
+              width: barWidth,
+              borderRadius: BorderRadius.all(Radius.zero),
+              borderSide: BorderSide(
+                color: barColor.darken(20),
+              ),
+            ),
+          ],
+        ));
+      }
+    });
+
+    return data;
+  }
+
+  FlBorderData getBorderData() {
+    return FlBorderData(
+      show: true,
+      border: Border.all(
+        color: AppColors.borderColor,
+        width: 2,
+      ),
+    );
+  }
+
+  FlGridData getGridData() {
+    return const FlGridData(
+      show: true,
+      drawHorizontalLine: true,
+      drawVerticalLine: false,
+    );
+  }
+
+  FlTitlesData getTitlesData() {
+    const AxisTitles none = const AxisTitles(
+      sideTitles: SideTitles(showTitles: false),
+    );
+
+    final AxisTitles verticalTitles = AxisTitles(
+      sideTitles: SideTitles(
+        showTitles: true,
+        reservedSize: 30,
+        getTitlesWidget: getVerticalTitlesWidget,
+        interval: 5,
+      ),
+    );
+    final AxisTitles horizontalTitles = AxisTitles(
+      sideTitles: SideTitles(
+        showTitles: true,
+        reservedSize: 20,
+        getTitlesWidget: getHorizontalTitlesWidget,
+      ),
+    );
+
+    return FlTitlesData(
+      show: true,
+      bottomTitles: horizontalTitles,
+      leftTitles: none,
+      topTitles: none,
+      rightTitles: verticalTitles,
+    );
+  }
+
+  Widget getVerticalTitlesWidget(double value, TitleMeta meta) {
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 4,
+      child: Text(
+        value.toInt().toString(),
+        style: const TextStyle(
+          color: AppColors.mainTextColor1,
+          fontSize: 12,
+        ),
+      ),
+    );
+  }
+
+  Widget getHorizontalTitlesWidget(double value, TitleMeta meta) {
+    String text = '';
+
+    if (value % 4 == 0 || value == 23) {
+      text = value.toInt().toString().padLeft(2, '0');
+    }
+
+    return SideTitleWidget(
+      axisSide: meta.axisSide,
+      space: 2,
+      child: Text(
+        text,
+        style: const TextStyle(
+          color: AppColors.mainTextColor1,
+          fontSize: 11,
+        ),
+      ),
+    );
+  }
+
+  BarTouchData getBarTouchData() {
+    return BarTouchData(
+      enabled: true,
+      touchTooltipData: BarTouchTooltipData(
+        tooltipBgColor: Colors.transparent,
+        tooltipPadding: EdgeInsets.zero,
+        tooltipMargin: 2,
+        getTooltipItem: (
+          BarChartGroupData group,
+          int groupIndex,
+          BarChartRodData rod,
+          int rodIndex,
+        ) {
+          return BarTooltipItem(
+            rod.toY.round().toString(),
+            const TextStyle(
+              color: AppColors.mainTextColor2,
+              fontWeight: FontWeight.bold,
+              fontSize: 10,
+            ),
+          );
+        },
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/main_screen/counts_by_hour_content.dart b/lib/ui/widgets/main_screen/counts_by_hour_content.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d53c34fcd8a3f0ecd994296d559564bbb63df351
--- /dev/null
+++ b/lib/ui/widgets/main_screen/counts_by_hour_content.dart
@@ -0,0 +1,43 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+import '../../../models/counts_by_hour.dart';
+import '../../../ui/widgets/main_screen/counts_by_hour_chart.dart';
+
+class ChartCountsByHourCardContent extends StatelessWidget {
+  final int daysCount;
+  final CountsByHourData chartData;
+  final bool isLoading;
+
+  const ChartCountsByHourCardContent(
+      {super.key, required this.daysCount, required this.chartData, required this.isLoading});
+
+  @override
+  Widget build(BuildContext context) {
+    final TextTheme textTheme = Theme.of(context).primaryTextTheme;
+    final String placeholder = '⏳';
+
+    return Column(
+      mainAxisSize: MainAxisSize.min,
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: <Widget>[
+        Text(
+          'counts_by_hour',
+          style: textTheme.titleLarge!.apply(fontWeightDelta: 2),
+        ).tr(
+          namedArgs: {
+            'daysCount': this.daysCount.toString(),
+          },
+        ),
+        const SizedBox(height: 8),
+        this.isLoading
+            ? Text(placeholder)
+            : CountsByHourCardContentChart(
+                chartData: CountsByHourData.fromJson(jsonDecode(this.chartData.toString())),
+              ),
+      ],
+    );
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index c2888d4362d054a8edf8d81c486e5e9bb4f23fd6..27e3de8e8f735ed1cc6fe6a38173279eaf6daaf8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Display scrobbles data and charts
 
 publish_to: 'none'
 
-version: 0.0.9+9
+version: 0.0.10+10
 
 environment:
   sdk: '^3.0.0'