diff --git a/packages/backend/migration/1644344266289-chart-v14.js b/packages/backend/migration/1644344266289-chart-v14.js
new file mode 100644
index 0000000000..8496cc2d42
--- /dev/null
+++ b/packages/backend/migration/1644344266289-chart-v14.js
@@ -0,0 +1,47 @@
+const { MigrationInterface, QueryRunner } = require("typeorm");
+
+module.exports = class chartV141644344266289 {
+    name = 'chartV141644344266289'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___write"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___write"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___read"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___read"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___readWrite"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___write"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___write"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___read"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___read"`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___readWrite"`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`);
+        await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`);
+    }
+}
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 927ce7c741..2639095f8a 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -80,7 +80,7 @@ export default define(meta, async (ps, user) => {
 
 	const timeline = await query.take(ps.limit!).getMany();
 
-	if (user) activeUsersChart.update(user);
+	if (user) activeUsersChart.read(user);
 
 	return await Notes.packMany(timeline, user);
 });
diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
index cac8b7d8a9..cdd110994e 100644
--- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts
@@ -96,7 +96,7 @@ export default define(meta, async (ps, user) => {
 
 	process.nextTick(() => {
 		if (user) {
-			activeUsersChart.update(user);
+			activeUsersChart.read(user);
 		}
 	});
 
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 9683df4611..b438491026 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -153,7 +153,7 @@ export default define(meta, async (ps, user) => {
 
 	process.nextTick(() => {
 		if (user) {
-			activeUsersChart.update(user);
+			activeUsersChart.read(user);
 		}
 	});
 
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 7776644124..ce0bcbeb7b 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -122,7 +122,7 @@ export default define(meta, async (ps, user) => {
 
 	process.nextTick(() => {
 		if (user) {
-			activeUsersChart.update(user);
+			activeUsersChart.read(user);
 		}
 	});
 
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 8be2861aec..f8cd083249 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -145,7 +145,7 @@ export default define(meta, async (ps, user) => {
 
 	process.nextTick(() => {
 		if (user) {
-			activeUsersChart.update(user);
+			activeUsersChart.read(user);
 		}
 	});
 
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 89de73fb9d..3512fb3638 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -142,7 +142,7 @@ export default define(meta, async (ps, user) => {
 
 	const timeline = await query.take(ps.limit!).getMany();
 
-	activeUsersChart.update(user);
+	activeUsersChart.read(user);
 
 	return await Notes.packMany(timeline, user);
 });
diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts
index 7fef99d3a9..87dd95f4dc 100644
--- a/packages/backend/src/services/chart/charts/active-users.ts
+++ b/packages/backend/src/services/chart/charts/active-users.ts
@@ -23,9 +23,9 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
 	}
 
 	@autobind
-	public async update(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
+	public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 		await this.commit({
-			'users': [user.id],
+			'read': [user.id],
 			'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
 			'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [],
 			'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [],
@@ -36,9 +36,9 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
 	}
 
 	@autobind
-	public async noted(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
+	public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> {
 		await this.commit({
-			'notedUsers': [user.id],
+			'write': [user.id],
 		});
 	}
 }
diff --git a/packages/backend/src/services/chart/charts/entities/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts
index ee16ef1278..843843836d 100644
--- a/packages/backend/src/services/chart/charts/entities/active-users.ts
+++ b/packages/backend/src/services/chart/charts/entities/active-users.ts
@@ -3,8 +3,9 @@ import Chart from '../../core';
 export const name = 'activeUsers';
 
 export const schema = {
-	'users': { uniqueIncrement: true },
-	'notedUsers': { uniqueIncrement: true, range: 'small' },
+	'readWrite': { intersection: ['read', 'write'], range: 'small' },
+	'read': { uniqueIncrement: true, range: 'small' },
+	'write': { uniqueIncrement: true, range: 'small' },
 	'registeredWithinWeek': { uniqueIncrement: true, range: 'small' },
 	'registeredWithinMonth': { uniqueIncrement: true, range: 'small' },
 	'registeredWithinYear': { uniqueIncrement: true, range: 'small' },
diff --git a/packages/backend/src/services/chart/charts/entities/test-intersection.ts b/packages/backend/src/services/chart/charts/entities/test-intersection.ts
new file mode 100644
index 0000000000..dc56eb93f5
--- /dev/null
+++ b/packages/backend/src/services/chart/charts/entities/test-intersection.ts
@@ -0,0 +1,11 @@
+import Chart from '../../core';
+
+export const name = 'testIntersection';
+
+export const schema = {
+	'a': { uniqueIncrement: true },
+	'b': { uniqueIncrement: true },
+	'aAndB': { intersection: ['a', 'b'] },
+} as const;
+
+export const entity = Chart.schemaToEntity(name, schema);
diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts
new file mode 100644
index 0000000000..c6ba71a956
--- /dev/null
+++ b/packages/backend/src/services/chart/charts/test-intersection.ts
@@ -0,0 +1,32 @@
+import autobind from 'autobind-decorator';
+import Chart, { KVs } from '../core';
+import { name, schema } from './entities/test-intersection';
+
+/**
+ * For testing
+ */
+// eslint-disable-next-line import/no-default-export
+export default class TestIntersectionChart extends Chart<typeof schema> {
+	constructor() {
+		super(name, schema);
+	}
+
+	@autobind
+	protected async queryCurrentState(): Promise<Partial<KVs<typeof schema>>> {
+		return {};
+	}
+
+	@autobind
+	public async addA(key: string): Promise<void> {
+		await this.commit({
+			a: [key],
+		});
+	}
+
+	@autobind
+	public async addB(key: string): Promise<void> {
+		await this.commit({
+			b: [key],
+		});
+	}
+}
diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts
index b6db598cfb..7032a09d39 100644
--- a/packages/backend/src/services/chart/core.ts
+++ b/packages/backend/src/services/chart/core.ts
@@ -46,6 +46,8 @@ const removeDuplicates = (array: any[]) => Array.from(new Set(array));
 type Schema = Record<string, {
 	uniqueIncrement?: boolean;
 
+	intersection?: string[] | ReadonlyArray<string>;
+
 	range?: 'big' | 'small' | 'medium';
 
 	// previousな値を引き継ぐかどうか
@@ -384,6 +386,33 @@ export default abstract class Chart<T extends Schema> {
 				}
 			}
 
+			// compute intersection
+			// TODO: intersectionに指定されたカラムがintersectionだった場合の対応
+			for (const [k, v] of Object.entries(this.schema)) {
+				const intersection = v.intersection;
+				if (intersection) {
+					const name = columnPrefix + k.replaceAll('.', columnDot);
+					const firstKey = intersection[0];
+					const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot);
+					const currentValuesForHour = new Set([...(finalDiffs[firstKey] ?? []), ...logHour[firstTempColumnName]]);
+					const currentValuesForDay = new Set([...(finalDiffs[firstKey] ?? []), ...logDay[firstTempColumnName]]);
+					for (let i = 1; i < intersection.length; i++) {
+						const targetKey = intersection[i];
+						const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot);
+						const targetValuesForHour = new Set([...(finalDiffs[targetKey] ?? []), ...logHour[targetTempColumnName]]);
+						const targetValuesForDay = new Set([...(finalDiffs[targetKey] ?? []), ...logDay[targetTempColumnName]]);
+						currentValuesForHour.forEach(v => {
+							if (!targetValuesForHour.has(v)) currentValuesForHour.delete(v);
+						});
+						currentValuesForDay.forEach(v => {
+							if (!targetValuesForDay.has(v)) currentValuesForDay.delete(v);
+						});
+					}
+					queryForHour[name] = currentValuesForHour.size;
+					queryForDay[name] = currentValuesForDay.size;
+				}
+			}
+
 			// ログ更新
 			await Promise.all([
 				this.repositoryForHour.createQueryBuilder()
diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts
index 53a86fb773..7a4c2cef12 100644
--- a/packages/backend/src/services/note/create.ts
+++ b/packages/backend/src/services/note/create.ts
@@ -297,7 +297,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
 	}
 
 	if (!silent) {
-		if (Users.isLocalUser(user)) activeUsersChart.noted(user);
+		if (Users.isLocalUser(user)) activeUsersChart.write(user);
 
 		// 未読通知を作成
 		if (data.visibility === 'specified') {
diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts
index 66000bc928..a869e0bae7 100644
--- a/packages/backend/test/chart.ts
+++ b/packages/backend/test/chart.ts
@@ -6,14 +6,17 @@ import { async, initTestDb } from './utils';
 import TestChart from '../src/services/chart/charts/test';
 import TestGroupedChart from '../src/services/chart/charts/test-grouped';
 import TestUniqueChart from '../src/services/chart/charts/test-unique';
+import TestIntersectionChart from '../src/services/chart/charts/test-intersection';
 import * as _TestChart from '../src/services/chart/charts/entities/test';
 import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped';
 import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique';
+import * as _TestIntersectionChart from '../src/services/chart/charts/entities/test-intersection';
 
 describe('Chart', () => {
 	let testChart: TestChart;
 	let testGroupedChart: TestGroupedChart;
 	let testUniqueChart: TestUniqueChart;
+	let testIntersectionChart: TestIntersectionChart;
 	let clock: lolex.Clock;
 
 	beforeEach(async(async () => {
@@ -21,11 +24,13 @@ describe('Chart', () => {
 			_TestChart.entity.hour, _TestChart.entity.day,
 			_TestGroupedChart.entity.hour, _TestGroupedChart.entity.day,
 			_TestUniqueChart.entity.hour, _TestUniqueChart.entity.day,
+			_TestIntersectionChart.entity.hour, _TestIntersectionChart.entity.day,
 		]);
 
 		testChart = new TestChart();
 		testGroupedChart = new TestGroupedChart();
 		testUniqueChart = new TestUniqueChart();
+		testIntersectionChart = new TestIntersectionChart();
 
 		clock = lolex.install({
 			now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0))
@@ -426,6 +431,45 @@ describe('Chart', () => {
 				foo: [2, 0, 0],
 			});
 		}));
+
+		describe('Intersection', () => {
+			it('条件が満たされていない場合はカウントされない', async(async () => {
+				await testIntersectionChart.addA('alice');
+				await testIntersectionChart.addA('bob');
+				await testIntersectionChart.addB('carol');
+				await testIntersectionChart.save();
+	
+				const chartHours = await testUniqueChart.getChart('hour', 3, null);
+				const chartDays = await testUniqueChart.getChart('day', 3, null);
+	
+				assert.deepStrictEqual(chartHours, {
+					aAndB: [0, 0, 0],
+				});
+	
+				assert.deepStrictEqual(chartDays, {
+					aAndB: [0, 0, 0],
+				});
+			}));
+
+			it('条件が満たされている場合にカウントされる', async(async () => {
+				await testIntersectionChart.addA('alice');
+				await testIntersectionChart.addA('bob');
+				await testIntersectionChart.addB('carol');
+				await testIntersectionChart.addB('alice');
+				await testIntersectionChart.save();
+	
+				const chartHours = await testUniqueChart.getChart('hour', 3, null);
+				const chartDays = await testUniqueChart.getChart('day', 3, null);
+	
+				assert.deepStrictEqual(chartHours, {
+					aAndB: [1, 0, 0],
+				});
+	
+				assert.deepStrictEqual(chartDays, {
+					aAndB: [1, 0, 0],
+				});
+			}));
+		});
 	});
 
 	describe('Resync', () => {
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index f297db7e13..1384789f8d 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -69,6 +69,7 @@ const colors = {
 	yellow: '#FEB019',
 	red: '#FF4560',
 	purple: '#e300db',
+	orange: '#fe6919',
 };
 const colorSets = [colors.blue, colors.green, colors.yellow, colors.red, colors.purple];
 const getColor = (i) => {
@@ -518,15 +519,20 @@ export default defineComponent({
 			const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
 			return {
 				series: [{
-					name: 'Active',
+					name: 'Read & Write',
 					type: 'area',
-					data: format(raw.users),
-					color: '#888888',
+					data: format(raw.readWrite),
+					color: colors.orange,
 				}, {
-					name: 'Noted',
+					name: 'Write',
 					type: 'area',
-					data: format(raw.notedUsers),
+					data: format(raw.write),
 					color: colors.blue,
+				}, {
+					name: 'Read',
+					type: 'area',
+					data: format(raw.read),
+					color: '#888888',
 				}, {
 					name: '< Week',
 					type: 'area',