Skip to main content
Version: V3

Semaphore groups

A Semaphore group contains identity commitments of group members. Example uses of groups include the following:

  • poll question that attendees join to rate an event,
  • ballot that members join to vote on a proposal,
  • whistleblowers who are verified employees of an organization.

A Semaphore group is an incremental Merkle tree, and group members (i.e., identity commitments) are tree leaves. Semaphore groups set the following two parameters:

  • Group id: a unique identifier for the group;
  • Tree depth: the maximum number of members a group can contain (max size = 2 ^ tree depth).

Learn how to work with groups.

Off-chain groups

Create a group

Use the @semaphore-protocol/group library Group class to create an off-chain group with the following parameters:

  • Group id: a unique identifier for the group;
  • Tree depth: (default 20) the maximum number of members a group can contain (max size = 2 ^ tree depth).

To create a group with default treeDepth, call the Group constructor without the second parameter. For example:

import { Group } from "@semaphore-protocol/group"

const group = new Group(1)

The following example code passes treeDepth to create a group for 2 ^ 30 = 1073741824 members:

import { Group } from "@semaphore-protocol/group"

const group = new Group(1, 30)

Add members

Use the Group addMember function to add a member (identity commitment) to a group. For example:


To add a batch of members to a group, pass an array to the Group addMembers function. For example:

group.addMembers([identityCommitment1, identityCommitment2])

When you use the same Semaphore identity across multiple groups, if an attacker takes control of that identity all the groups it is part of will be compromised. Consider using different identities for each group.

Remove or update members

To remove members from a group, pass the member index to the Group removeMember function. For example:


To update members in a group, pass the member index and the new value to the Group updateMember function. For example:

group.updateMember(0, 2)

Removing a member from a group sets the node value to a special value (i.e. zeroValue). Given that the node isn't removed, and the length of the group.members array doesn't change.

On-chain groups

The SemaphoreGroups contract uses the IncrementalBinaryTree library and provides methods to create and manage groups.


You can import SemaphoreGroups.sol and other Semaphore contracts from the @semaphore-protocol/contracts NPM module.

Alternatively, you can use an already deployed Semaphore.sol contract and use its group external functions.


Semaphore.sol does not check if a member with a specific identity commitment already exists in a group. This check must be done off-chain.


Semaphore.sol includes a mechanism to verify Semaphore proofs created with old Merkle tree roots, the duration of which can be defined by the admin in the createGroup function. Members of a group could then continue to generate valid proofs even after being removed. For more info see the issue #98.